[mini] Initial tiered compilation work (mono/mono#17551)
authorBernhard Urban-Forster <lewurm@gmail.com>
Tue, 29 Oct 2019 21:36:01 +0000 (22:36 +0100)
committermonojenkins <jo.shields+jenkins@xamarin.com>
Tue, 29 Oct 2019 21:36:01 +0000 (22:36 +0100)
[mini] Initial tiered compilation work

Enable it with `./autogen.sh --enable-experiment=tiered`.

Let's consider `Simple.cs`:
```csharp
using System.Runtime.CompilerServices;
using System;

public class Simple {
    public static void Main (string []args) {
        HotMethod ();
        Console.WriteLine ("cnt: " + cnt);
        HotMethod ();
        Console.WriteLine ("cnt: " + cnt);
    }

    static int cnt = 0;

    [MethodImplAttribute (MethodImplOptions.NoInlining)]
    public static void HotMethod () {
        for (int i = 0; i <= 1000; i++)
            cnt += i;
    }
}
```

```console
$ csc Simple.cs
$ MONO_LOG_LEVEL=debug MONO_LOG_MASK=tiered ./mono/mini/mono-sgen --trace=M:Simple:HotMethod --interp=-all Simple.exe
[0x10ec275c0: 0.00000 0] ENTER:i Simple:HotMethod ()()
Mono: tiered: queued Simple:HotMethod ()
[0x10ec275c0: 0.00010 0] LEAVE:i Simple:HotMethod ()(
Mono: tiered: patching 0x7fe855803224 with patch_kind=INTERP @ tier_level=0
Mono:   -> caller= Simple:Main (string[]) [{0x7fe85420df88} + 0x34 interp]  (0x7fe8558031f0 0x7fe855803258) [0x7fe85420c880 - Simple.exe]
Mono:   -> callee=Simple:HotMethod ()
Mono: tiered: patching 0x7fe8558031f2 with patch_kind=INTERP @ tier_level=0
Mono:   -> caller= Simple:Main (string[]) [{0x7fe85420df88} + 0x2 interp]  (0x7fe8558031f0 0x7fe855803258) [0x7fe85420c880 - Simple.exe]
Mono:   -> callee=Simple:HotMethod ()
cnt: 500500
[0x10ec275c0: 0.04093 0] ENTER:c Simple:HotMethod ()()
[0x10ec275c0: 0.04095 0] LEAVE:c Simple:HotMethod ()(
cnt: 1001000
```
Note the suffix after `ENTER:`
* `i` indicates it's executed by the interpreter
* `c` indicates it's a compiled method (JIT)

Another example:
```console
$ make -C mono/mini gshared.exe
$ ./mono/mini/mono-sgen --interp=-all --stats ./mono/mini/gshared.exe
Regression tests: 84 ran, 0 failed in Tests
[...]
Tiered statistics
Methods promoted                    : 68
```

It's a basic proof-of-concept for now. An incomplete list of future work items:

* Right now it only works for direct calls, need to expand it to virtual/interface calls.
* Calls of the JIT leading into the interpreter again can't be patched yet.
* Kind of related, no concept of versioning compiled methods does exist yet. The interpreter maintains its own table of "transformed" methods, however, when doing the transition from JIT->interpreter, the wrapper+interpmethod will end up in the JIT table. We need a way to have multiple JIT compilation results for the same MonoMethod exist.
* Investigate actual performance. We might have to optimize the interp<>JIT transition.
* Patching is racy. Need to make that atomic.
* All the FIXMEs in this PR

<!--
Thank you for your Pull Request!

If you are new to contributing to Mono, please try to do your best at conforming to our coding guidelines http://www.mono-project.com/community/contributing/coding-guidelines/ but don't worry if you get something wrong. One of the project members will help you to get things landed.

Does your pull request fix any of the existing issues? Please use the following format: Fixes #issue-number
-->

Commit migrated from https://github.com/mono/mono/commit/d274cfbacdb2aeab0184662997882f3d78991e48

18 files changed:
src/mono/configure.ac
src/mono/mono/mini/Makefile.am.in
src/mono/mono/mini/interp/interp-internals.h
src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/interp/mintops.def
src/mono/mono/mini/interp/mintops.h
src/mono/mono/mini/interp/transform.c
src/mono/mono/mini/mini-profiler.c
src/mono/mono/mini/mini-runtime.c
src/mono/mono/mini/mini.h
src/mono/mono/mini/tiered.c [new file with mode: 0644]
src/mono/mono/mini/tiered.h [new file with mode: 0644]
src/mono/mono/mini/trace.c
src/mono/mono/mini/trace.h
src/mono/mono/utils/mono-counters.c
src/mono/mono/utils/mono-counters.h
src/mono/mono/utils/mono-logger-internals.h
src/mono/mono/utils/mono-logger.c

index b5384c2..7ab5aa4 100644 (file)
@@ -5691,7 +5691,7 @@ dnl *** feature experiments ***
 dnl ***************************
 
 dnl When adding experiments, also add to mono/utils/mono-experiments.def
-AC_ARG_ENABLE(experiment, [ --enable-experiment=LIST       Enable experimental fatures from the comma-separate LIST.  Available experiments: null],[
+AC_ARG_ENABLE(experiment, [ --enable-experiment=LIST       Enable experimental fatures from the comma-separate LIST.  Available experiments: null,tiered],[
 
        if test x$enable_experiment != x ; then
                AC_DEFINE(ENABLE_EXPERIMENTS,1,[Enable feature experiments])
@@ -5702,14 +5702,20 @@ AC_ARG_ENABLE(experiment, [ --enable-experiment=LIST       Enable experimental f
 
        if test "x$mono_experiment_test_enable_all" = "xyes"; then
                eval "mono_experiment_test_enable_null='yes'"
+               eval "mono_experiment_test_enable_tiered='yes'"
                true
        fi
 
        if test "x$mono_experiment_test_enable_null" = "xyes"; then
                AC_DEFINE(ENABLE_EXPERIMENT_null, 1, [Enable experiment 'null'])
        fi
+       if test "x$mono_experiment_test_enable_tiered" = "xyes"; then
+               AC_DEFINE(ENABLE_EXPERIMENT_TIERED, 1, [Enable experiment 'Tiered Compilation'])
+       fi
 ],[])
 
+AM_CONDITIONAL(ENABLE_EXPERIMENT_TIERED, test x$mono_experiment_test_enable_tiered = xyes)
+
 dnl **********************
 dnl *** checked builds ***
 dnl **********************
index 0c66de7..ba93c8c 100755 (executable)
@@ -551,6 +551,8 @@ common_sources = \
        llvmonly-runtime.c \
        aot-runtime.h   \
        ee.h \
+       tiered.h \
+       tiered.c \
        mini-runtime.h
 
 endif # !ENABLE_MSVC_ONLY
index 26b16a6..060a610 100644 (file)
@@ -169,6 +169,9 @@ typedef struct _InterpMethod
        MonoJitInfo *jinfo;
        MonoDomain *domain;
        MonoProfilerCallInstrumentationFlags prof_flags;
+#ifdef ENABLE_EXPERIMENT_TIERED
+       MiniTieredCounter tiered_counter;
+#endif
 } InterpMethod;
 
 struct _InterpFrame {
@@ -198,6 +201,7 @@ typedef struct {
 
 typedef struct {
        gint64 transform_time;
+       gint64 methods_transformed;
        gint64 cprop_time;
        gint64 super_instructions_time;
        gint32 stloc_nps;
index 9a62539..0b2ae13 100644 (file)
@@ -2105,7 +2105,7 @@ do_jit_call (stackval *sp, unsigned char *vt_sp, ThreadContext *context, InterpF
         * Call JITted code through a gsharedvt_out wrapper. These wrappers receive every argument
         * by ref and return a return value using an explicit return value argument.
         */
-       if (!rmethod->jit_wrapper) {
+       if (G_UNLIKELY (!rmethod->jit_wrapper)) {
                MonoMethod *method = rmethod->method;
 
                sig = mono_method_signature_internal (method);
@@ -3305,6 +3305,9 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                sp++;
        }
 
+#ifdef ENABLE_EXPERIMENT_TIERED
+       mini_tiered_inc (frame->imethod->domain, frame->imethod->method, &frame->imethod->tiered_counter, 0);
+#endif
        //g_print ("(%p) Call %s\n", mono_thread_internal_current (), mono_method_get_full_name (frame->imethod->method));
 
        /*
@@ -3625,7 +3628,12 @@ main_loop:
                        goto common_vcall;
                }
                MINT_IN_CASE(MINT_CALL)
-                       sp = mono_interp_call (frame, context, &child_frame, (ip += 2) - 2, sp, vt_sp, FALSE);
+                       sp = mono_interp_call (frame, context, &child_frame, ip, sp, vt_sp, FALSE);
+#ifdef ENABLE_EXPERIMENT_TIERED
+                       ip += 5;
+#else
+                       ip += 2;
+#endif
 common_call:
                        child_frame.stack_args = sp;
                        interp_exec_method (&child_frame, context, error);
@@ -3638,7 +3646,12 @@ vcall_return:
                        MINT_IN_BREAK;
 
                MINT_IN_CASE(MINT_VCALL)
-                       sp = mono_interp_call (frame, context, &child_frame, (ip += 2) - 2, sp, vt_sp, FALSE);
+                       sp = mono_interp_call (frame, context, &child_frame, ip, sp, vt_sp, FALSE);
+#ifdef ENABLE_EXPERIMENT_TIERED
+                       ip += 5;
+#else
+                       ip += 2;
+#endif
 common_vcall:
                        child_frame.stack_args = sp;
                        interp_exec_method (&child_frame, context, error);
@@ -3670,6 +3683,29 @@ common_vcall:
 
                        MINT_IN_BREAK;
                }
+               MINT_IN_CASE(MINT_JIT_CALL2) {
+#ifdef ENABLE_EXPERIMENT_TIERED
+                       InterpMethod *rmethod = (InterpMethod *) READ64 (ip + 1);
+
+                       MONO_API_ERROR_INIT (error);
+                       frame->ip = ip;
+
+                       sp = do_jit_call (sp, vt_sp, context, frame, rmethod, error);
+                       if (!is_ok (error)) {
+                               MonoException *ex = mono_error_convert_to_exception (error);
+                               THROW_EX (ex, ip);
+                       }
+                       ip += 5;
+
+                       CHECK_RESUME_STATE (context);
+
+                       if (rmethod->rtype->type != MONO_TYPE_VOID)
+                               sp++;
+#else
+                       g_error ("MINT_JIT_ICALL2 shouldn't be used");
+#endif
+                       MINT_IN_BREAK;
+               }
                MINT_IN_CASE(MINT_CALLRUN) {
 #ifndef ENABLE_NETCORE
                        MonoMethod *target_method = (MonoMethod*) frame->imethod->data_items [ip [1]];
@@ -3715,24 +3751,45 @@ common_vcall:
                                g_warning_d ("ret.vt: more values on stack: %d", sp - frame->stack);
                        goto exit_frame;
                }
-               MINT_IN_CASE(MINT_BR_S)
-                       ip += (short) *(ip + 1);
+
+#ifdef ENABLE_EXPERIMENT_TIERED
+#define BACK_BRANCH_PROFILE(offset) do { \
+               if (offset < 0) \
+                       mini_tiered_inc (frame->imethod->domain, frame->imethod->method, &frame->imethod->tiered_counter, 0); \
+       } while (0);
+#else
+#define BACK_BRANCH_PROFILE(offset)
+#endif
+
+               MINT_IN_CASE(MINT_BR_S) {
+                       short br_offset = (short) *(ip + 1);
+                       BACK_BRANCH_PROFILE (br_offset);
+                       ip += br_offset;
                        MINT_IN_BREAK;
-               MINT_IN_CASE(MINT_BR)
-                       ip += (gint32) READ32(ip + 1);
+               }
+               MINT_IN_CASE(MINT_BR) {
+                       gint32 br_offset = (gint32) READ32(ip + 1);
+                       BACK_BRANCH_PROFILE (br_offset);
+                       ip += br_offset;
                        MINT_IN_BREAK;
+               }
+
 #define ZEROP_S(datamem, op) \
        --sp; \
-       if (sp->data.datamem op 0) \
-               ip += (gint16)ip [1]; \
-       else \
+       if (sp->data.datamem op 0) { \
+               gint16 br_offset = (gint16) ip [1]; \
+               BACK_BRANCH_PROFILE (br_offset); \
+               ip += br_offset; \
+       } else \
                ip += 2;
 
 #define ZEROP(datamem, op) \
        --sp; \
-       if (sp->data.datamem op 0) \
-               ip += (gint32)READ32(ip + 1); \
-       else \
+       if (sp->data.datamem op 0) { \
+               gint32 br_offset = (gint32)READ32(ip + 1); \
+               BACK_BRANCH_PROFILE (br_offset); \
+               ip += br_offset; \
+       } else \
                ip += 3;
 
                MINT_IN_CASE(MINT_BRFALSE_I4_S)
@@ -3785,18 +3842,22 @@ common_vcall:
                        MINT_IN_BREAK;
 #define CONDBR_S(cond) \
        sp -= 2; \
-       if (cond) \
-               ip += (gint16)ip [1]; \
-       else \
+       if (cond) { \
+               gint16 br_offset = (gint16) ip [1]; \
+               BACK_BRANCH_PROFILE (br_offset); \
+               ip += br_offset; \
+       } else \
                ip += 2;
 #define BRELOP_S(datamem, op) \
        CONDBR_S(sp[0].data.datamem op sp[1].data.datamem)
 
 #define CONDBR(cond) \
        sp -= 2; \
-       if (cond) \
-               ip += (gint32)READ32(ip + 1); \
-       else \
+       if (cond) { \
+               gint32 br_offset = (gint32) READ32 (ip + 1); \
+               BACK_BRANCH_PROFILE (br_offset); \
+               ip += br_offset; \
+       } else \
                ip += 3;
 
 #define BRELOP(datamem, op) \
@@ -3949,16 +4010,20 @@ common_vcall:
 
 #define BRELOP_S_CAST(datamem, op, type) \
        sp -= 2; \
-       if ((type) sp[0].data.datamem op (type) sp[1].data.datamem) \
-               ip += (gint16)ip [1]; \
-       else \
+       if ((type) sp[0].data.datamem op (type) sp[1].data.datamem) { \
+               gint16 br_offset = (gint16) ip [1]; \
+               BACK_BRANCH_PROFILE (br_offset); \
+               ip += br_offset; \
+       } else \
                ip += 2;
 
 #define BRELOP_CAST(datamem, op, type) \
        sp -= 2; \
-       if ((type) sp[0].data.datamem op (type) sp[1].data.datamem) \
-               ip += (gint32)READ32(ip + 1); \
-       else \
+       if ((type) sp[0].data.datamem op (type) sp[1].data.datamem) { \
+               gint32 br_offset = (gint32) ip [1]; \
+               BACK_BRANCH_PROFILE (br_offset); \
+               ip += br_offset; \
+       } else \
                ip += 3;
 
                MINT_IN_CASE(MINT_BGE_UN_I4_S)
@@ -6357,7 +6422,7 @@ common_vcall:
                        prof_ctx->interp_frame = frame;
                        prof_ctx->method = frame->imethod->method;
 
-                       mono_trace_enter_method (frame->imethod->method, prof_ctx);
+                       mono_trace_enter_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx);
                        MINT_IN_BREAK;
                }
 
@@ -6378,7 +6443,7 @@ common_vcall:
                        prof_ctx->interp_frame = frame;
                        prof_ctx->method = frame->imethod->method;
 
-                       mono_trace_leave_method (frame->imethod->method, prof_ctx);
+                       mono_trace_leave_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx);
                        ip += 3;
                        goto exit_frame;
                }
@@ -7058,6 +7123,7 @@ register_interp_stats (void)
 {
        mono_counters_init ();
        mono_counters_register ("Total transform time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.transform_time);
+       mono_counters_register ("Methods transformed", MONO_COUNTER_INTERP | MONO_COUNTER_LONG, &mono_interp_stats.methods_transformed);
        mono_counters_register ("Total cprop time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.cprop_time);
        mono_counters_register ("Total super instructions time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.super_instructions_time);
        mono_counters_register ("STLOC_NP count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.stloc_nps);
index ef83487..51f8dc8 100644 (file)
@@ -734,6 +734,7 @@ OPDEF(MINT_ICALL_PPPPPP_V, "mono_icall_pppppp_v", 2, Pop6, Push0, MintOpClassTok
 OPDEF(MINT_ICALL_PPPPPP_P, "mono_icall_pppppp_p", 2, Pop6, Push1, MintOpClassToken)
 // FIXME: MintOp
 OPDEF(MINT_JIT_CALL, "mono_jit_call", 2, VarPop, VarPush, MintOpNoArgs)
+OPDEF(MINT_JIT_CALL2, "mono_jit_call2", 5, VarPop, VarPush, MintOpNoArgs)
 
 OPDEF(MINT_MONO_LDPTR, "mono_ldptr", 2, Pop0, Push1, MintOpClassToken)
 OPDEF(MINT_MONO_SGEN_THREAD_INFO, "mono_sgen_thread_info", 1, Pop0, Push1, MintOpNoArgs)
index 69c4239..9e3c3b6 100644 (file)
@@ -60,6 +60,7 @@ typedef enum {
 #define MINT_IS_STLOC_NP(op) ((op) >= MINT_STLOC_NP_I4 && (op) <= MINT_STLOC_NP_O)
 #define MINT_IS_CONDITIONAL_BRANCH(op) ((op) >= MINT_BRFALSE_I4 && (op) <= MINT_BLT_UN_R8_S)
 #define MINT_IS_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_JIT_CALL)
+#define MINT_IS_PATCHABLE_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_VCALL)
 #define MINT_IS_NEWOBJ(op) ((op) >= MINT_NEWOBJ && (op) <= MINT_NEWOBJ_MAGIC)
 #define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_M1 && (op) <= MINT_LDC_I4)
 #define MINT_IS_UNOP(op) ((op) >= MINT_ADD1_I4 && (op) <= MINT_CEQ0_I4)
index de88b7b..81d9dd3 100644 (file)
@@ -36,6 +36,7 @@
 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY 2
 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4
 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8
+#define INTERP_INST_FLAG_RECORD_CALL_PATCH 16
 
 #define INTERP_LOCAL_FLAG_INDIRECT 1
 #define INTERP_LOCAL_FLAG_DEAD 2
@@ -157,6 +158,9 @@ typedef struct
        int max_data_items;
        void **data_items;
        GHashTable *data_hash;
+#ifdef ENABLE_EXPERIMENT_TIERED
+       GHashTable *patchsite_hash;
+#endif
        int *clause_indexes;
        gboolean gen_sdb_seq_points;
        GPtrArray *seq_points;
@@ -437,6 +441,14 @@ interp_prev_ins (InterpInst *ins)
                (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
        } while (0)
 
+#define WRITE64(ins, v) \
+       do { \
+               *((ins) + 0) = * ((guint16 *)(v) + 0); \
+               *((ins) + 1) = * ((guint16 *)(v) + 1); \
+               *((ins) + 2) = * ((guint16 *)(v) + 2); \
+               *((ins) + 3) = * ((guint16 *)(v) + 3); \
+       } while (0)
+
 #define WRITE64_INS(ins, index, v) \
        do { \
                (ins)->data [index] = * (guint16 *)(v); \
@@ -455,6 +467,11 @@ interp_prev_ins (InterpInst *ins)
                * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
        } while (0)
 
+#define WRITE64(ip, v) \
+       do { \
+               * (guint64*)(ip) = * (guint64 *)(v); \
+               (ip) += 4; \
+       } while (0)
 #define WRITE64_INS(ins, index, v) \
        do { \
                * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
@@ -887,6 +904,27 @@ jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
        return FALSE;
 }
 
+#ifdef ENABLE_EXPERIMENT_TIERED
+static gboolean
+jit_call2_supported (MonoMethod *method, MonoMethodSignature *sig)
+{
+       if (sig->param_count > 6)
+               return FALSE;
+       if (sig->pinvoke)
+               return FALSE;
+       if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
+               return FALSE;
+       if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
+               return FALSE;
+       if (method->is_inflated)
+               return FALSE;
+       if (method->string_ctor)
+               return FALSE;
+
+       return TRUE;
+}
+#endif
+
 static int mono_class_get_magic_index (MonoClass *k)
 {
        if (mono_class_is_magic_int (k))
@@ -2451,6 +2489,13 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
                        }
                } else {
                        td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
+#ifdef ENABLE_EXPERIMENT_TIERED
+                       if (MINT_IS_PATCHABLE_CALL (td->last_ins->opcode)) {
+                               g_assert (!calli && !is_virtual);
+                               td->last_ins->flags |= INTERP_INST_FLAG_RECORD_CALL_PATCH;
+                               g_hash_table_insert (td->patchsite_hash, td->last_ins, target_method);
+                       }
+#endif
                        return_val_if_nok (error, FALSE);
                        if (csignature->call_convention == MONO_CALL_VARARG)
                                td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
@@ -3274,7 +3319,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
 
        if (sym_seq_points) {
                last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
-               last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
+               last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
        }
 
        if (mono_debugger_method_has_breakpoint (method))
@@ -3685,7 +3730,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
 
                        if (sym_seq_points) {
                                last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
-                               td->last_ins->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
+                               td->last_ins->flags |= INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
                        }
 
                        if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
@@ -6207,6 +6252,10 @@ get_inst_length (InterpInst *ins)
 {
        if (ins->opcode == MINT_SWITCH)
                return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
+#ifdef ENABLE_EXPERIMENT_TIERED
+       else if (MINT_IS_PATCHABLE_CALL (ins->opcode))
+               return MAX (mono_interp_oplen [MINT_JIT_CALL2], mono_interp_oplen [ins->opcode]);
+#endif
        else
                return mono_interp_oplen [ins->opcode];
 }
@@ -6397,6 +6446,30 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in
 
                cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
                cbb->last_seq_point = seqp;
+#ifdef ENABLE_EXPERIMENT_TIERED
+       } else if (ins->flags & INTERP_INST_FLAG_RECORD_CALL_PATCH) {
+               g_assert (MINT_IS_PATCHABLE_CALL (opcode));
+
+               /* TODO: could `ins` be removed by any interp optimization? */
+               MonoMethod *target_method = (MonoMethod *) g_hash_table_lookup (td->patchsite_hash, ins);
+               g_assert (target_method);
+               g_hash_table_remove (td->patchsite_hash, ins);
+
+               mini_tiered_record_callsite (start_ip, target_method, TIERED_PATCH_KIND_INTERP);
+
+               int size = mono_interp_oplen [ins->opcode];
+               int jit_call2_size = mono_interp_oplen [MINT_JIT_CALL2];
+
+               g_assert (size < jit_call2_size);
+
+               // Emit the rest of the data
+               for (int i = 0; i < size - 1; i++)
+                       *ip++ = ins->data [i];
+
+               /* intentional padding so we can patch a MINT_JIT_CALL2 here */
+               for (int i = size - 1; i < (jit_call2_size - 1); i++)
+                       *ip++ = MINT_NIY;
+#endif
        } else {
                if (MINT_IS_LDLOC (opcode) || MINT_IS_STLOC (opcode) || MINT_IS_STLOC_NP (opcode) || opcode == MINT_LDLOCA_S ||
                                MINT_IS_LDLOCFLD (opcode) || MINT_IS_LOCUNOP (opcode) || MINT_IS_STLOCFLD (opcode)) {
@@ -7245,6 +7318,9 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
        td->max_data_items = 0;
        td->data_items = NULL;
        td->data_hash = g_hash_table_new (NULL, NULL);
+#ifdef ENABLE_EXPERIMENT_TIERED
+       td->patchsite_hash = g_hash_table_new (NULL, NULL);
+#endif
        td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
        td->seq_points = g_ptr_array_new ();
        td->verbose_level = mono_interp_traceopt;
@@ -7353,6 +7429,10 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
        }
 
        save_seq_points (td, jinfo);
+#ifdef ENABLE_EXPERIMENT_TIERED
+       /* debugging aid, it makes `mono_pmip` work. */
+       mono_jit_info_table_add (domain, jinfo);
+#endif
 
 exit:
        g_free (td->in_offsets);
@@ -7367,6 +7447,9 @@ exit:
        g_free (td->is_bb_start);
        g_free (td->locals);
        g_hash_table_destroy (td->data_hash);
+#ifdef ENABLE_EXPERIMENT_TIERED
+       g_hash_table_destroy (td->patchsite_hash);
+#endif
        g_ptr_array_free (td->seq_points, TRUE);
        g_array_free (td->line_numbers, TRUE);
        mono_mempool_destroy (td->mempool);
@@ -7374,10 +7457,40 @@ exit:
 
 static mono_mutex_t calc_section;
 
+#ifdef ENABLE_EXPERIMENT_TIERED
+static gboolean
+tiered_patcher (MiniTieredPatchPointContext *ctx, gpointer patchsite)
+{
+       ERROR_DECL (error);
+       MonoMethod *m = ctx->target_method;
+
+       if (!jit_call2_supported (m, mono_method_signature_internal (m)))
+               return FALSE;
+
+       /* TODO: Force compilation here. Currently the JIT will be invoked upon
+        *       first execution of `MINT_JIT_CALL2`. */
+       InterpMethod *rmethod = mono_interp_get_imethod (ctx->domain, m, error);
+       mono_error_assert_ok (error);
+
+       guint16 *ip = ((guint16 *) patchsite);
+       *ip++ = MINT_JIT_CALL2;
+       /* FIXME: this only works on 64bit */
+       WRITE64 (ip, &rmethod);
+       mono_memory_barrier ();
+
+       return TRUE;
+}
+#endif
+
+
 void 
 mono_interp_transform_init (void)
 {
        mono_os_mutex_init_recursive(&calc_section);
+
+#ifdef ENABLE_EXPERIMENT_TIERED
+       mini_tiered_register_callsite_patcher (tiered_patcher, TIERED_PATCH_KIND_INTERP);
+#endif
 }
 
 void
@@ -7459,6 +7572,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon
                        imethod->alloca_size = imethod->stack_size;
                        mono_memory_barrier ();
                        imethod->transformed = TRUE;
+                       mono_interp_stats.methods_transformed++;
                        mono_os_mutex_unlock (&calc_section);
                        MONO_PROFILER_RAISE (jit_done, (method, NULL));
                        return;
@@ -7497,6 +7611,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon
                memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
                mono_memory_barrier ();
                imethod->transformed = TRUE;
+               mono_interp_stats.methods_transformed++;
                mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
 
        }
index e677dd6..8f70714 100644 (file)
@@ -92,16 +92,17 @@ mini_profiler_emit_enter (MonoCompile *cfg)
        if (cfg->current_method != cfg->method)
                return;
 
-       MonoInst *iargs [2];
+       MonoInst *iargs [3];
 
        EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
+       EMIT_NEW_PCONST (cfg, iargs [1], NULL);
 
        if (MONO_CFG_PROFILE (cfg, ENTER_CONTEXT))
-               iargs [1] = emit_fill_call_ctx (cfg, iargs [0], NULL);
+               iargs [2] = emit_fill_call_ctx (cfg, iargs [0], NULL);
        else
-               EMIT_NEW_PCONST (cfg, iargs [1], NULL);
+               EMIT_NEW_PCONST (cfg, iargs [2], NULL);
 
-       /* void mono_profiler_raise_method_enter (MonoMethod *method, MonoProfilerCallContext *ctx) */
+       /* void mono_profiler_raise_method_enter (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */
        if (trace)
                mono_emit_jit_icall (cfg, mono_trace_enter_method, iargs);
        else
@@ -116,16 +117,17 @@ mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret)
        if (!MONO_CFG_PROFILE (cfg, LEAVE) || cfg->current_method != cfg->method || (cfg->compile_aot && !can_encode_method_ref (cfg->method)))
                return;
 
-       MonoInst *iargs [2];
+       MonoInst *iargs [3];
 
        EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
+       EMIT_NEW_PCONST (cfg, iargs [1], NULL);
 
        if (MONO_CFG_PROFILE (cfg, LEAVE_CONTEXT))
-               iargs [1] = emit_fill_call_ctx (cfg, iargs [0], ret);
+               iargs [2] = emit_fill_call_ctx (cfg, iargs [0], ret);
        else
-               EMIT_NEW_PCONST (cfg, iargs [1], NULL);
+               EMIT_NEW_PCONST (cfg, iargs [2], NULL);
 
-       /* void mono_profiler_raise_method_leave (MonoMethod *method, MonoProfilerCallContext *ctx) */
+       /* void mono_profiler_raise_method_leave (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx) */
        if (trace)
                mono_emit_jit_icall (cfg, mono_trace_leave_method, iargs);
        else
@@ -142,14 +144,15 @@ mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target)
 
        g_assert (cfg->current_method == cfg->method);
 
-       MonoInst *iargs [2];
+       MonoInst *iargs [3];
 
        EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
+       EMIT_NEW_PCONST (cfg, iargs [1], NULL);
 
        if (target)
-               EMIT_NEW_METHODCONST (cfg, iargs [1], target);
+               EMIT_NEW_METHODCONST (cfg, iargs [2], target);
        else
-               EMIT_NEW_PCONST (cfg, iargs [1], NULL);
+               EMIT_NEW_PCONST (cfg, iargs [2], NULL);
 
        /* void mono_profiler_raise_method_tail_call (MonoMethod *method, MonoMethod *target) */
        if (trace)
index 91e1d16..8e644d1 100644 (file)
@@ -232,7 +232,9 @@ mono_get_method_from_ip (void *ip)
        if (location)
                file_loc = g_strdup_printf ("[%s :: %du]", location->source_file, location->row);
 
-       res = g_strdup_printf (" %s [{%p} + 0x%x] %s (%p %p) [%p - %s]", method_name, method, (int)((char*)ip - (char*)ji->code_start), file_loc ? file_loc : "", ji->code_start, (char*)ji->code_start + ji->code_size, domain, domain->friendly_name);
+       const char *in_interp = ji->is_interp ? " interp" : "";
+
+       res = g_strdup_printf (" %s [{%p} + 0x%x%s] %s (%p %p) [%p - %s]", method_name, method, (int)((char*)ip - (char*)ji->code_start), in_interp, file_loc ? file_loc : "", ji->code_start, (char*)ji->code_start + ji->code_size, domain, domain->friendly_name);
 
        mono_debug_free_source_location (location);
        g_free (method_name);
@@ -4382,6 +4384,13 @@ mini_init (const char *filename, const char *runtime_version)
        MONO_PROFILER_RAISE (thread_name, (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()), "Main"));
 #endif
 
+#ifdef ENABLE_EXPERIMENT_TIERED
+       if (!mono_compile_aot) {
+               /* create compilation thread in background */
+               mini_tiered_init ();
+       }
+#endif
+
        if (mono_profiler_sampling_enabled ())
                mono_runtime_setup_stat_profiler ();
 
@@ -4421,8 +4430,8 @@ register_icalls (void)
        register_icall (mono_profiler_raise_method_tail_call, mono_icall_sig_void_ptr_ptr, TRUE);
        register_icall (mono_profiler_raise_exception_clause, mono_icall_sig_void_ptr_int_int_object, TRUE);
 
-       register_icall (mono_trace_enter_method, mono_icall_sig_void_ptr_ptr, TRUE);
-       register_icall (mono_trace_leave_method, mono_icall_sig_void_ptr_ptr, TRUE);
+       register_icall (mono_trace_enter_method, mono_icall_sig_void_ptr_ptr_ptr, TRUE);
+       register_icall (mono_trace_leave_method, mono_icall_sig_void_ptr_ptr_ptr, TRUE);
        g_assert (mono_get_lmf_addr == mono_tls_get_lmf_addr);
        register_icall (mono_jit_set_domain, mono_icall_sig_void_ptr, TRUE);
        register_icall (mono_domain_get, mono_icall_sig_ptr, TRUE);
index db89b70..0a7a98b 100644 (file)
@@ -52,6 +52,7 @@ typedef struct SeqPointInfo SeqPointInfo;
 #include "mini-unwind.h"
 #include "jit.h"
 #include "cfgdump.h"
+#include "tiered.h"
 
 #include "mono/metadata/tabledefs.h"
 #include "mono/metadata/marshal.h"
diff --git a/src/mono/mono/mini/tiered.c b/src/mono/mono/mini/tiered.c
new file mode 100644 (file)
index 0000000..3de1541
--- /dev/null
@@ -0,0 +1,141 @@
+#include <config.h>
+#include <mono/utils/mono-compiler.h>
+
+#include "mini.h"
+#include "mini-runtime.h"
+#include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-logger-internals.h>
+
+#ifdef ENABLE_EXPERIMENT_TIERED
+
+MiniTieredStats mini_tiered_stats;
+
+static MonoCoopCond compilation_wait;
+static MonoCoopMutex compilation_mutex;
+
+#define NUM_TIERS 2
+
+static GSList *compilation_queue [NUM_TIERS];
+static CallsitePatcher patchers [NUM_TIERS] = { NULL };
+
+static const char* const patch_kind_str[] = {
+       "INTERP",
+       "JIT",
+};
+
+static GHashTable *callsites_hash [TIERED_PATCH_KIND_NUM] = { NULL };
+
+/* TODO: use scientific methods (TM) to determine values */
+static const int threshold [NUM_TIERS] = {
+       1000, /* tier 0 */
+       3000, /* tier 1 */
+};
+
+static void
+compiler_thread (void)
+{
+       MonoInternalThread *internal = mono_thread_internal_current ();
+       internal->state |= ThreadState_Background;
+       internal->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
+
+       mono_native_thread_set_name (mono_native_thread_id_get (), "Tiered Compilation Thread");
+
+       while (TRUE) {
+               mono_coop_cond_wait (&compilation_wait, &compilation_mutex);
+
+               for (int tier_level = 0; tier_level < NUM_TIERS; tier_level++) {
+                       GSList *ppcs = compilation_queue [tier_level];
+                       compilation_queue [tier_level] = NULL;
+
+                       for (GSList *ppc_= ppcs; ppc_ != NULL; ppc_ = ppc_->next) {
+                               MiniTieredPatchPointContext *ppc = (MiniTieredPatchPointContext *) ppc_->data;
+
+                               for (int patch_kind = 0; patch_kind < TIERED_PATCH_KIND_NUM; patch_kind++) {
+                                       if (!callsites_hash [patch_kind])
+                                               continue;
+
+                                       GSList *patchsites = g_hash_table_lookup (callsites_hash [patch_kind], ppc->target_method);
+
+                                       for (; patchsites != NULL; patchsites = patchsites->next) {
+                                               gpointer patchsite = (gpointer) patchsites->data;
+
+                                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_TIERED, "tiered: patching %p with patch_kind=%s @ tier_level=%d", patchsite, patch_kind_str [patch_kind], tier_level);
+                                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_TIERED, "\t-> caller=%s", mono_pmip (patchsite));
+                                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_TIERED, "\t-> callee=%s", mono_method_full_name (ppc->target_method, TRUE));
+
+                                               gboolean success = patchers [patch_kind] (ppc, patchsite);
+
+                                               if (!success)
+                                                       mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_TIERED, "tiered: couldn't patch %p with target %s, dropping it.", patchsite, mono_method_full_name (ppc->target_method, TRUE));
+                                       }
+                                       g_hash_table_remove (callsites_hash [patch_kind], ppc->target_method);
+                                       g_slist_free (patchsites);
+                               }
+                               g_free (ppc);
+                       }
+                       g_slist_free (ppcs);
+               }
+               mono_coop_mutex_unlock (&compilation_mutex);
+       }
+}
+
+void
+mini_tiered_init (void)
+{
+       ERROR_DECL (error);
+
+       mono_counters_init ();
+       mono_counters_register ("Methods promoted", MONO_COUNTER_TIERED | MONO_COUNTER_LONG, &mini_tiered_stats.methods_promoted);
+
+       mono_coop_cond_init (&compilation_wait);
+       mono_coop_mutex_init (&compilation_mutex);
+
+       mono_thread_create_internal (mono_domain_get (), compiler_thread, NULL, MONO_THREAD_CREATE_FLAGS_THREADPOOL, error);
+       mono_error_assert_ok (error);
+}
+
+void
+mini_tiered_register_callsite_patcher (CallsitePatcher func, int level)
+{
+       g_assert (level < NUM_TIERS);
+
+       patchers [level] = func;
+}
+
+void
+mini_tiered_record_callsite (gpointer ip, MonoMethod *target_method, int patch_kind)
+{
+       if (!callsites_hash [patch_kind])
+               callsites_hash [patch_kind] = g_hash_table_new (NULL, NULL);
+
+       GSList *patchsites = g_hash_table_lookup (callsites_hash [patch_kind], target_method);
+       patchsites = g_slist_prepend (patchsites, ip);
+       g_hash_table_insert (callsites_hash [patch_kind], target_method, patchsites);
+}
+
+void
+mini_tiered_inc (MonoDomain *domain, MonoMethod *method, MiniTieredCounter *tcnt, int tier_level)
+{
+       if (G_UNLIKELY (tcnt->hotness == threshold [tier_level] && !tcnt->promoted)) {
+               tcnt->promoted = TRUE;
+               mini_tiered_stats.methods_promoted++;
+
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_TIERED, "tiered: queued %s", mono_method_full_name (method, TRUE));
+
+               MiniTieredPatchPointContext *ppc = g_new0 (MiniTieredPatchPointContext, 1);
+               ppc->domain = domain;
+               ppc->target_method = method;
+               ppc->tier_level = tier_level;
+
+               mono_coop_mutex_lock (&compilation_mutex);
+               compilation_queue [tier_level] = g_slist_append (compilation_queue [tier_level], ppc);
+               mono_coop_mutex_unlock (&compilation_mutex);
+               mono_coop_cond_signal (&compilation_wait);
+       } else if (!tcnt->promoted) {
+               /* FIXME: inline that into caller */
+               tcnt->hotness++;
+       }
+}
+#else
+MONO_EMPTY_SOURCE_FILE (tiered);
+#endif
diff --git a/src/mono/mono/mini/tiered.h b/src/mono/mono/mini/tiered.h
new file mode 100644 (file)
index 0000000..91e91da
--- /dev/null
@@ -0,0 +1,40 @@
+#ifdef ENABLE_EXPERIMENT_TIERED
+
+#ifndef __MONO_MINI_TIERED_H__
+#define __MONO_MINI_TIERED_H__
+
+#define TIERED_PATCH_KIND_INTERP 0
+#define TIERED_PATCH_KIND_JIT 1
+#define TIERED_PATCH_KIND_NUM 2
+
+typedef struct {
+       int hotness;
+       gboolean promoted;
+} MiniTieredCounter;
+
+typedef struct {
+       gint64 methods_promoted;
+} MiniTieredStats;
+
+typedef struct {
+       MonoDomain *domain;
+       MonoMethod *target_method;
+       int tier_level;
+} MiniTieredPatchPointContext;
+
+typedef gboolean (*CallsitePatcher)(MiniTieredPatchPointContext *context, gpointer patchsite);
+
+void
+mini_tiered_init (void);
+
+void
+mini_tiered_inc (MonoDomain *domain, MonoMethod *method, MiniTieredCounter *tcnt, int level);
+
+void
+mini_tiered_record_callsite (gpointer callsite, MonoMethod *target_method, int level);
+
+void
+mini_tiered_register_callsite_patcher (CallsitePatcher func, int level);
+
+#endif /* __MONO_MINI_TIERED_H__ */
+#endif /* ENABLE_EXPERIMENT_TIERED */
index 3832620..5aada9f 100644 (file)
@@ -111,8 +111,16 @@ string_to_utf8 (MonoString *s)
  */
 #define arg_in_stack_slot(cpos, type) ((type *)(cpos))
 
+static gboolean
+is_gshared_vt_wrapper (MonoMethod *m)
+{
+       if (m->wrapper_type != MONO_WRAPPER_OTHER)
+               return FALSE;
+       return !strcmp (m->name, "interp_in") || !strcmp (m->name, "gsharedvt_out_sig");
+}
+
 void
-mono_trace_enter_method (MonoMethod *method, MonoProfilerCallContext *ctx)
+mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx)
 {
        int i;
        MonoClass *klass;
@@ -130,14 +138,21 @@ mono_trace_enter_method (MonoMethod *method, MonoProfilerCallContext *ctx)
        while (output_lock != 0 || mono_atomic_cas_i32 (&output_lock, 1, 0) != 0)
                mono_thread_info_yield ();
 
-       printf ("ENTER: %s(", fname);
+
+       /* FIXME: Might be better to pass the ji itself */
+       if (!ji)
+               ji = mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL);
+
+       /* ENTER:i <- interp
+        * ENTER:c <- compiled (JIT or AOT)
+        */
+       printf ("ENTER:%c %s(", ji->is_interp ? 'i' : 'c' , fname);
        g_free (fname);
 
        sig = mono_method_signature_internal (method);
 
        if (method->is_inflated) {
-               /* FIXME: Might be better to pass the ji itself */
-               MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL);
+
                if (ji) {
                        gsctx = mono_jit_info_get_generic_sharing_context (ji);
                        if (gsctx && gsctx->is_gsharedvt) {
@@ -151,7 +166,7 @@ mono_trace_enter_method (MonoMethod *method, MonoProfilerCallContext *ctx)
 
        if (sig->hasthis) {
                void *this_buf = mini_profiler_context_get_this (ctx);
-               if (m_class_is_valuetype (method->klass)) {
+               if (m_class_is_valuetype (method->klass) || is_gshared_vt_wrapper (method)) {
                        printf ("value:%p", this_buf);
                } else {
                        MonoObject *o = *(MonoObject**)this_buf;
@@ -289,7 +304,7 @@ mono_trace_enter_method (MonoMethod *method, MonoProfilerCallContext *ctx)
 }
 
 void
-mono_trace_leave_method (MonoMethod *method, MonoProfilerCallContext *ctx)
+mono_trace_leave_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx)
 {
        MonoType *type;
        char *fname;
@@ -304,12 +319,17 @@ mono_trace_leave_method (MonoMethod *method, MonoProfilerCallContext *ctx)
        while (output_lock != 0 || mono_atomic_cas_i32 (&output_lock, 1, 0) != 0)
                mono_thread_info_yield ();
 
-       printf ("LEAVE: %s", fname);
+       /* FIXME: Might be better to pass the ji itself from the JIT */
+       if (!ji)
+               ji = mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL);
+
+       /* LEAVE:i <- interp
+        * LEAVE:c <- compiled (JIT or AOT)
+        */
+       printf ("LEAVE:%c %s(", ji->is_interp ? 'i' : 'c' , fname);
        g_free (fname);
 
        if (method->is_inflated) {
-               /* FIXME: Might be better to pass the ji itself */
-               MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL);
                if (ji) {
                        gsctx = mono_jit_info_get_generic_sharing_context (ji);
                        if (gsctx && gsctx->is_gsharedvt) {
index 86be5f9..8a53d08 100644 (file)
 
 ICALL_EXTERN_C
 void
-mono_trace_enter_method (MonoMethod *method, MonoProfilerCallContext *ctx);
+mono_trace_enter_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx);
 
 ICALL_EXTERN_C
 void 
-mono_trace_leave_method (MonoMethod *method, MonoProfilerCallContext *ctx);
+mono_trace_leave_method (MonoMethod *method, MonoJitInfo *ji, MonoProfilerCallContext *ctx);
 
 void mono_trace_enable (gboolean enable);
 gboolean mono_trace_is_enabled (void);
index 012aa16..7bf6ccb 100644 (file)
@@ -572,6 +572,7 @@ section_names [][12] = {
        "", // MONO_COUNTER_PERFCOUNTERS - not used.
        "Profiler",
        "Interp",
+       "Tiered",
 };
 
 static void
index eb261d6..77175b3 100644 (file)
@@ -32,6 +32,7 @@ enum {
        MONO_COUNTER_PERFCOUNTERS = 1 << 15,
        MONO_COUNTER_PROFILER = 1 << 16,
        MONO_COUNTER_INTERP   = 1 << 17,
+       MONO_COUNTER_TIERED   = 1 << 18,
        MONO_COUNTER_LAST_SECTION,
 
        /* Unit, bits 24-27 (4 bits) */
index 86ce493..8e15efa 100644 (file)
@@ -28,6 +28,7 @@ typedef enum {
        MONO_TRACE_IO_LAYER_HANDLE    = 1 << 15,
        MONO_TRACE_TAILCALL           = 1 << 16,
        MONO_TRACE_PROFILER           = 1 << 17,
+       MONO_TRACE_TIERED             = 1 << 18,
 } MonoTraceMask;
 
 MONO_API_DATA GLogLevelFlags mono_internal_current_level;
index 89de7ed..174a227 100644 (file)
@@ -314,6 +314,7 @@ mono_trace_set_mask_string (const char *value)
                { "w32handle", MONO_TRACE_IO_LAYER_HANDLE },
                { "tailcall", MONO_TRACE_TAILCALL },
                { "profiler", MONO_TRACE_PROFILER },
+               { "tiered", MONO_TRACE_TIERED },
                { "all", (MonoTraceMask)~0 }, // FIXMEcxx there is a better way -- operator overloads of enums
                { NULL, (MonoTraceMask)0 },
        };