[interp] initial infrastructure for whitebox testing of optimization passes (mono...
authorBernhard Urban-Forster <lewurm@gmail.com>
Tue, 14 Jan 2020 20:59:48 +0000 (21:59 +0100)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Tue, 14 Jan 2020 20:59:48 +0000 (21:59 +0100)
Commit migrated from https://github.com/mono/mono/commit/05f2651a12ba5eae1de8b488c4026145622b265a

src/mono/mono/mini/.gitignore
src/mono/mono/mini/Makefile.am.in
src/mono/mono/mini/interp/transform.c
src/mono/mono/mini/interp/transform.h [new file with mode: 0644]
src/mono/mono/mini/interp/whitebox-snippets.il [new file with mode: 0644]
src/mono/mono/mini/interp/whitebox.c [new file with mode: 0644]

index af434f4..9a71425 100644 (file)
@@ -34,3 +34,4 @@
 /buildver-sgen.h
 /buildver-boehm.h
 /regressiontests.out
+/test-mono-interp-whitebox
index 15568a9..c56d31f 100755 (executable)
@@ -779,6 +779,20 @@ libmonosgen_2_0_la_CFLAGS = $(mono_sgen_CFLAGS) @CXX_ADD_CFLAGS@
 libmonosgen_2_0_la_LIBADD = libmini.la $(interp_libs_with_mini) $(dbg_libs_with_mini) $(sgen_libs) $(LIBMONO_DTRACE_OBJECT) $(LLVMMONOF)
 libmonosgen_2_0_la_LDFLAGS = $(libmonoldflags) $(monobin_platform_ldflags) 
 
+noinst_LIBRARIES += libmaintest.a
+
+libmaintest_a_SOURCES = interp/whitebox.c
+libmaintest_a_CFLAGS = $(AM_CFLAGS) @CXX_ADD_CFLAGS@
+
+WHITEBOX_ASSEMBLY=interp/whitebox-snippets.exe
+test_mono_interp_whitebox_SOURCES =
+test_mono_interp_whitebox_CFLAGS = $(AM_CFLAGS) @CXX_REMOVE_CFLAGS@
+test_mono_interp_whitebox_LDADD = interp/libmaintest_a-whitebox.$(OBJEXT) libmini.la $(interp_libs_with_mini) $(dbg_libs_with_mini) $(sgen_libs)
+test_mono_interp_whitebox_LDFLAGS = $(libmonoldflags) $(monobin_platform_ldflags)
+test_mono_interp_whitebox_DEPENDENCIES = $(WHITEBOX_ASSEMBLY)
+
+check_PROGRAMS = test-mono-interp-whitebox
+
 endif # !ENABLE_MSVC_ONLY
 
 libmonoincludedir = $(includedir)/mono-$(API_VER)/mono/jit
@@ -900,6 +914,9 @@ rcheck: mono $(regtests)
 richeck: mono $(regtests)
        $(INTERP_RUNTIME) --regression $(regtests)
 
+interp-whitebox: test-mono-interp-whitebox
+       MONO_PATH=$(CLASS) ./$< $(WHITEBOX_ASSEMBLY)
+
 mixedcheck: mono mixed.exe
        $(MINI_RUNTIME) --interp=jit=JitClass mixed.exe
 
index ede4709..4acba8d 100644 (file)
@@ -18,7 +18,6 @@
 #include <mono/metadata/marshal.h>
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/tabledefs.h>
-#include <mono/metadata/seq-points-data.h>
 #include <mono/metadata/mono-basic-block.h>
 #include <mono/metadata/abi-details.h>
 #include <mono/metadata/reflection-internals.h>
 #include "mintops.h"
 #include "interp-internals.h"
 #include "interp.h"
-
-#define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
-#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
+#include "transform.h"
 
 MonoInterpStats mono_interp_stats;
 
 #define DEBUG 0
 
-typedef struct InterpInst InterpInst;
-
-typedef struct
-{
-       MonoClass *klass;
-       unsigned char type;
-       unsigned char flags;
-} StackInfo;
-
-#define STACK_VALUE_NONE 0
-#define STACK_VALUE_LOCAL 1
-#define STACK_VALUE_ARG 2
-#define STACK_VALUE_I4 3
-#define STACK_VALUE_I8 4
-
-// StackValue contains data to construct an InterpInst that is equivalent with the contents
-// of the stack slot / local / argument.
-typedef struct {
-       // Indicates the type of the stored information. It can be a local, argument or a constant
-       int type;
-       // Holds the local index or the actual constant value
-       union {
-               int local;
-               int arg;
-               gint32 i;
-               gint64 l;
-       };
-} StackValue;
-
-typedef struct
-{
-       // This indicates what is currently stored in this stack slot. This can be a constant
-       // or the copy of a local / argument.
-       StackValue val;
-       // The instruction that pushed this stack slot. If ins is null, we can't remove the usage
-       // of the stack slot, because we can't clear the instruction that set it.
-       InterpInst *ins;
-} StackContentInfo;
-
-struct InterpInst {
-       guint16 opcode;
-       InterpInst *next, *prev;
-       // If this is -1, this instruction is not logically associated with an IL offset, it is
-       // part of the IL instruction associated with the previous interp instruction.
-       int il_offset;
-       guint32 flags;
-       guint16 data [MONO_ZERO_LEN_ARRAY];
-};
-
-typedef struct {
-       guint8 *ip;
-       GSList *preds;
-       GSList *seq_points;
-       SeqPoint *last_seq_point;
-
-       // This will hold a list of last sequence points of incoming basic blocks
-       SeqPoint **pred_seq_points;
-       guint num_pred_seq_points;
-} InterpBasicBlock;
-
-typedef enum {
-       RELOC_SHORT_BRANCH,
-       RELOC_LONG_BRANCH,
-       RELOC_SWITCH
-} RelocType;
-
-typedef struct {
-       RelocType type;
-       /* In the interpreter IR */
-       int offset;
-       /* In the IL code */
-       int target;
-} Reloc;
-
-typedef struct {
-       MonoType *type;
-       int mt;
-       int flags;
-       int offset;
-} InterpLocal;
-
-typedef struct
-{
-       MonoMethod *method;
-       MonoMethod *inlined_method;
-       MonoMethodHeader *header;
-       InterpMethod *rtm;
-       const unsigned char *il_code;
-       const unsigned char *ip;
-       const unsigned char *in_start;
-       InterpInst *last_ins, *first_ins;
-       int code_size;
-       int *in_offsets;
-       int current_il_offset;
-       StackInfo **stack_state;
-       int *stack_height;
-       int *vt_stack_size;
-       unsigned char *is_bb_start;
-       unsigned short *new_code;
-       unsigned short *new_code_end;
-       unsigned int max_code_size;
-       StackInfo *stack;
-       StackInfo *sp;
-       unsigned int max_stack_height;
-       unsigned int stack_capacity;
-       unsigned int vt_sp;
-       unsigned int max_vt_sp;
-       unsigned int total_locals_size;
-       InterpLocal *locals;
-       unsigned int locals_size;
-       unsigned int locals_capacity;
-       int n_data_items;
-       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;
-       InterpBasicBlock **offset_to_bb;
-       InterpBasicBlock *entry_bb;
-       MonoMemPool     *mempool;
-       GList *basic_blocks;
-       GPtrArray *relocs;
-       gboolean verbose_level;
-       GArray *line_numbers;
-} TransformData;
-
-#define STACK_TYPE_I4 0
-#define STACK_TYPE_I8 1
-#define STACK_TYPE_R4 2
-#define STACK_TYPE_R8 3
-#define STACK_TYPE_O  4
-#define STACK_TYPE_VT 5
-#define STACK_TYPE_MP 6
-#define STACK_TYPE_F  7
-
-static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
-
-#if SIZEOF_VOID_P == 8
-#define STACK_TYPE_I STACK_TYPE_I8
-#else
-#define STACK_TYPE_I STACK_TYPE_I4
-#endif
-
-static int stack_type [] = {
-       STACK_TYPE_I4, /*I1*/
-       STACK_TYPE_I4, /*U1*/
-       STACK_TYPE_I4, /*I2*/
-       STACK_TYPE_I4, /*U2*/
-       STACK_TYPE_I4, /*I4*/
-       STACK_TYPE_I8, /*I8*/
-       STACK_TYPE_R4, /*R4*/
-       STACK_TYPE_R8, /*R8*/
-       STACK_TYPE_O,  /*O*/
-       STACK_TYPE_MP, /*P*/
-       STACK_TYPE_VT
-};
-
 #if SIZEOF_VOID_P == 8
 #define MINT_NEG_P MINT_NEG_I8
 #define MINT_NOT_P MINT_NOT_I8
@@ -1054,6 +884,21 @@ mono_interp_print_code (InterpMethod *imethod)
        dump_mint_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size));
 }
 
+/* For debug use */
+void
+mono_interp_print_td_code (TransformData *td)
+{
+       InterpInst *ins = td->first_ins;
+
+       char *name = mono_method_full_name (td->method, TRUE);
+       g_print ("IR for \"%s\"\n", name);
+       g_free (name);
+       while (ins) {
+               dump_interp_inst_newline (ins);
+               ins = ins->next;
+       }
+}
+
 
 static MonoMethodHeader*
 interp_method_get_header (MonoMethod* method, MonoError *error)
@@ -2987,6 +2832,12 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet
        td->total_locals_size = offset;
 }
 
+void
+mono_test_interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
+{
+       interp_method_compute_offsets (td, imethod, signature, header);
+}
+
 /* Return false is failure to init basic blocks due to being in inline method */
 static gboolean
 init_bb_start (TransformData *td, MonoMethodHeader *header, gboolean inlining)
@@ -7328,6 +7179,12 @@ retry:
        g_free (local_ref_count);
 }
 
+void
+mono_test_interp_cprop (TransformData *td)
+{
+       interp_cprop (td);
+}
+
 static void
 interp_super_instructions (TransformData *td)
 {
@@ -7583,6 +7440,12 @@ exit:
        mono_mempool_destroy (td->mempool);
 }
 
+gboolean
+mono_test_interp_generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
+{
+       return generate_code (td, method, header, generic_context, error);
+}
+
 static mono_mutex_t calc_section;
 
 #ifdef ENABLE_EXPERIMENT_TIERED
diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h
new file mode 100644 (file)
index 0000000..9f40463
--- /dev/null
@@ -0,0 +1,189 @@
+#ifndef __MONO_MINI_INTERP_TRANSFORM_H__
+#define __MONO_MINI_INTERP_TRANSFORM_H__
+#include <mono/mini/mini-runtime.h>
+#include <mono/metadata/seq-points-data.h>
+#include "interp-internals.h"
+
+#define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
+#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
+
+typedef struct InterpInst InterpInst;
+
+typedef struct
+{
+       MonoClass *klass;
+       unsigned char type;
+       unsigned char flags;
+} StackInfo;
+
+#define STACK_VALUE_NONE 0
+#define STACK_VALUE_LOCAL 1
+#define STACK_VALUE_ARG 2
+#define STACK_VALUE_I4 3
+#define STACK_VALUE_I8 4
+
+// StackValue contains data to construct an InterpInst that is equivalent with the contents
+// of the stack slot / local / argument.
+typedef struct {
+       // Indicates the type of the stored information. It can be a local, argument or a constant
+       int type;
+       // Holds the local index or the actual constant value
+       union {
+               int local;
+               int arg;
+               gint32 i;
+               gint64 l;
+       };
+} StackValue;
+
+typedef struct
+{
+       // This indicates what is currently stored in this stack slot. This can be a constant
+       // or the copy of a local / argument.
+       StackValue val;
+       // The instruction that pushed this stack slot. If ins is null, we can't remove the usage
+       // of the stack slot, because we can't clear the instruction that set it.
+       InterpInst *ins;
+} StackContentInfo;
+
+struct InterpInst {
+       guint16 opcode;
+       InterpInst *next, *prev;
+       // If this is -1, this instruction is not logically associated with an IL offset, it is
+       // part of the IL instruction associated with the previous interp instruction.
+       int il_offset;
+       guint32 flags;
+       guint16 data [MONO_ZERO_LEN_ARRAY];
+};
+
+typedef struct {
+       guint8 *ip;
+       GSList *preds;
+       GSList *seq_points;
+       SeqPoint *last_seq_point;
+
+       // This will hold a list of last sequence points of incoming basic blocks
+       SeqPoint **pred_seq_points;
+       guint num_pred_seq_points;
+} InterpBasicBlock;
+
+typedef enum {
+       RELOC_SHORT_BRANCH,
+       RELOC_LONG_BRANCH,
+       RELOC_SWITCH
+} RelocType;
+
+typedef struct {
+       RelocType type;
+       /* In the interpreter IR */
+       int offset;
+       /* In the IL code */
+       int target;
+} Reloc;
+
+typedef struct {
+       MonoType *type;
+       int mt;
+       int flags;
+       int offset;
+} InterpLocal;
+
+typedef struct
+{
+       MonoMethod *method;
+       MonoMethod *inlined_method;
+       MonoMethodHeader *header;
+       InterpMethod *rtm;
+       const unsigned char *il_code;
+       const unsigned char *ip;
+       const unsigned char *in_start;
+       InterpInst *last_ins, *first_ins;
+       int code_size;
+       int *in_offsets;
+       int current_il_offset;
+       StackInfo **stack_state;
+       int *stack_height;
+       int *vt_stack_size;
+       unsigned char *is_bb_start;
+       unsigned short *new_code;
+       unsigned short *new_code_end;
+       unsigned int max_code_size;
+       StackInfo *stack;
+       StackInfo *sp;
+       unsigned int max_stack_height;
+       unsigned int stack_capacity;
+       unsigned int vt_sp;
+       unsigned int max_vt_sp;
+       unsigned int total_locals_size;
+       InterpLocal *locals;
+       unsigned int locals_size;
+       unsigned int locals_capacity;
+       int n_data_items;
+       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;
+       InterpBasicBlock **offset_to_bb;
+       InterpBasicBlock *entry_bb;
+       MonoMemPool     *mempool;
+       GList *basic_blocks;
+       GPtrArray *relocs;
+       gboolean verbose_level;
+       GArray *line_numbers;
+} TransformData;
+
+#define STACK_TYPE_I4 0
+#define STACK_TYPE_I8 1
+#define STACK_TYPE_R4 2
+#define STACK_TYPE_R8 3
+#define STACK_TYPE_O  4
+#define STACK_TYPE_VT 5
+#define STACK_TYPE_MP 6
+#define STACK_TYPE_F  7
+
+static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
+
+#if SIZEOF_VOID_P == 8
+#define STACK_TYPE_I STACK_TYPE_I8
+#else
+#define STACK_TYPE_I STACK_TYPE_I4
+#endif
+
+static int stack_type [] = {
+       STACK_TYPE_I4, /*I1*/
+       STACK_TYPE_I4, /*U1*/
+       STACK_TYPE_I4, /*I2*/
+       STACK_TYPE_I4, /*U2*/
+       STACK_TYPE_I4, /*I4*/
+       STACK_TYPE_I8, /*I8*/
+       STACK_TYPE_R4, /*R4*/
+       STACK_TYPE_R8, /*R8*/
+       STACK_TYPE_O,  /*O*/
+       STACK_TYPE_MP, /*P*/
+       STACK_TYPE_VT
+};
+
+/* test exports for white box testing */
+void
+mono_test_interp_cprop (TransformData *td);
+gboolean
+mono_test_interp_generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
+void
+mono_test_interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header);
+
+/* debugging aid */
+void
+mono_interp_print_td_code (TransformData *td);
+
+#endif /* __MONO_MINI_INTERP_TRANSFORM_H__ */
diff --git a/src/mono/mono/mini/interp/whitebox-snippets.il b/src/mono/mono/mini/interp/whitebox-snippets.il
new file mode 100644 (file)
index 0000000..554c28e
--- /dev/null
@@ -0,0 +1,36 @@
+.assembly snippets {}
+.assembly extern mscorlib {}
+
+.class public auto ansi sealed beforefieldinit Snippets {
+
+       .method static public int32 Main(string[] args) il managed {
+               .entrypoint
+               ldc.i4.s 0x1337
+               ret
+       }
+
+       .method public hidebysig static int32 no_inline_int32 () cil managed noinlining {
+               .maxstack 0
+               ldc.i4 0x1337
+               ret
+       }
+
+       .method public hidebysig static int32 test_cprop_add_consts () cil managed {
+               ldc.i4 0x1122
+               ldc.i4 0x3344
+               add
+               ret
+       }
+
+       .method public hidebysig static int32 test_cprop_ldloc_stloc () cil managed {
+               .locals init (
+                       int32 i
+               )
+               call int32 class Snippets::no_inline_int32 ()
+               stloc 0
+               ldloc 0
+               ldloc 0
+               add
+               ret
+       }
+}
diff --git a/src/mono/mono/mini/interp/whitebox.c b/src/mono/mono/mini/interp/whitebox.c
new file mode 100644 (file)
index 0000000..24db2dc
--- /dev/null
@@ -0,0 +1,236 @@
+#include <config.h>
+#include <stdio.h>
+#include "transform.h"
+#include "mintops.h"
+#include <mono/metadata/assembly-internals.h>
+#include <mono/metadata/mono-config.h>
+#include <mono/metadata/gc-internals.h>
+
+/* return value of "0" equals success */
+typedef int (*TestVerifier)(TransformData *td);
+
+typedef struct
+{
+       const char *test_name;
+       /* function pointer to result verifier */
+       TestVerifier verify_td;
+} TestItem;
+
+static MonoMemPool *mp = NULL;
+static GList *test_list = NULL;
+static const char *verbose_method_name = NULL;
+
+static void
+print_td (TransformData *td)
+{
+       if (!td->verbose_level)
+               return;
+
+       mono_interp_print_td_code (td);
+}
+
+static int
+expect (InterpInst **ins, InterpInst **current, guint16 opcode)
+{
+       g_assert (ins);
+
+       if (!*ins)
+               return 1;
+
+       while ((*ins)->opcode == MINT_NOP)
+               *ins = (*ins)->next;
+
+       if ((*ins)->opcode == opcode) {
+               if (current)
+                       *current = *ins;
+
+               *ins = (*ins)->next;
+               return 0;
+       }
+       return 2;
+}
+
+static int
+verify_cprop_add_consts (TransformData *td)
+{
+       mono_test_interp_cprop (td);
+       print_td (td);
+
+       InterpInst *ins = td->first_ins, *current;
+       if (expect (&ins, &current, MINT_LDC_I4))
+               return 1;
+       if (READ32 (&current->data [0]) != 0x4466)
+               return 2;
+       if (expect (&ins, NULL, MINT_RET))
+               return 3;
+
+       return 0;
+}
+
+static int
+verify_cprop_ldloc_stloc (TransformData *td)
+{
+       mono_test_interp_cprop (td);
+       print_td (td);
+
+       InterpInst *ins = td->first_ins;
+       if (expect (&ins, NULL, MINT_INITLOCALS))
+               return 1;
+       if (expect (&ins, NULL, MINT_CALL))
+               return 2;
+       if (expect (&ins, NULL, MINT_STLOC_NP_I4))
+               return 3;
+       if (expect (&ins, NULL, MINT_LDLOC_I4))
+               return 4;
+       if (expect (&ins, NULL, MINT_ADD_I4))
+               return 5;
+       if (expect (&ins, NULL, MINT_RET))
+               return 6;
+
+       return 0;
+}
+
+static void
+new_test (const char *name, TestVerifier verifier)
+{
+       TestItem *ti = g_malloc (sizeof (TestItem));
+       ti->test_name = name;
+       ti->verify_td = verifier;
+
+       test_list = g_list_append_mempool (mp, test_list, ti);
+}
+
+static MonoImage *
+load_assembly (const char *path, MonoDomain *root_domain)
+{
+       MonoAssemblyOpenRequest req;
+       mono_assembly_request_prepare_open (&req, MONO_ASMCTX_DEFAULT, mono_domain_default_alc (root_domain));
+       MonoAssembly *ass = mono_assembly_request_open (path, &req, NULL);
+       if (!ass)
+               g_error ("failed to load assembly: %s", path);
+       return mono_assembly_get_image_internal (ass);
+}
+
+static MonoMethod *
+lookup_method_from_image (MonoImage *image, const char *name)
+{
+       for (int i = 0; i < mono_image_get_table_rows (image, MONO_TABLE_METHOD); i++) {
+               ERROR_DECL (error);
+               MonoMethod *method = mono_get_method_checked (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL, NULL, error);
+               if (strcmp (method->name, name) == 0) {
+                       mono_class_init_internal (method->klass);
+                       return method;
+               }
+       }
+       g_error ("method \"%s\" does not exist in assembly \"%s\\n", name, image->assembly_name);
+}
+
+static int
+determine_verbose_level (TransformData *td)
+{
+       if (!verbose_method_name)
+               return 0;
+
+       if (!strcmp ("ALL", verbose_method_name))
+               return 4;
+
+       MonoMethod *method = td->method;
+       const char *name = verbose_method_name;
+
+       if ((strchr (name, '.') > name) || strchr (name, ':')) {
+               MonoMethodDesc *desc = mono_method_desc_new (name, TRUE);
+               int match = mono_method_desc_full_match (desc, method);
+               mono_method_desc_free (desc);
+               if (match)
+                       return 4;
+       } else {
+               if (strcmp (method->name, name) == 0)
+                       return 4;
+       }
+
+       return 0;
+}
+
+static TransformData *
+transform_method (MonoDomain *domain, MonoImage *image, TestItem *ti)
+{
+       ERROR_DECL (error);
+       MonoMethod *method = lookup_method_from_image (image, ti->test_name);
+       MonoMethodHeader *header = mono_method_get_header_checked (method, error);;;
+       MonoMethodSignature *signature = mono_method_signature_internal (method);
+
+       InterpMethod *rtm = g_new0 (InterpMethod, 1);
+       rtm->method = method;
+       rtm->domain = domain;
+       /* TODO: init more fields of `rtm` */
+
+       TransformData *td = g_new0 (TransformData, 1);
+       td->method = method;
+       td->verbose_level = determine_verbose_level (td);
+       td->mempool = mp;
+       td->rtm = rtm;
+       td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
+       td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
+       td->is_bb_start = (guint8*)g_malloc0(header->code_size);
+       td->data_items = NULL;
+       td->data_hash = g_hash_table_new (NULL, NULL);
+       /* TODO: init more fields of `td` */
+
+       mono_test_interp_method_compute_offsets (td, rtm, signature, header);
+
+       td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
+       td->stack_capacity = header->max_stack + 1;
+       td->sp = td->stack;
+       td->max_stack_height = 0;
+
+       mono_test_interp_generate_code (td, method, header, NULL, error);
+
+       mono_metadata_free_mh (header);
+       return td;
+}
+
+int
+main (int argc, char* argv[])
+{
+       if (argc < 2)
+               g_error ("need to pass whitebox assembly");
+
+       int test_failed = 0, test_success = 0;
+       char *whitebox_assembly = argv [1];
+       mp = mono_mempool_new ();
+
+       /* test list */
+       new_test ("test_cprop_add_consts", verify_cprop_add_consts);
+       new_test ("test_cprop_ldloc_stloc", verify_cprop_ldloc_stloc);
+
+       /* init mono runtime */
+       g_set_prgname (argv [0]);
+       mono_set_rootdir ();
+       mono_config_parse (NULL);
+       MonoDomain *root_domain = mini_init ("whitebox", NULL);
+       mono_gc_set_stack_end (&root_domain);
+
+       verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
+
+       g_print ("interp opt white box testing suite with %s, running %d tests\n", whitebox_assembly, g_list_length (test_list));
+
+       MonoImage *image = load_assembly (whitebox_assembly, root_domain);
+
+       for (GList *iter = test_list; iter; iter = iter->next) {
+               TestItem *ti = (TestItem *) iter->data;
+               TransformData *td = transform_method (root_domain, image, ti);
+               int result = ti->verify_td (td);
+               g_print ("test \"%s\": %d\n", ti->test_name, result);
+               free (td);
+
+               if (!!result)
+                       test_failed++;
+               else
+                       test_success++;
+       }
+
+       g_print ("\nSUMMARY: %d / %d passed\n", test_success, test_success + test_failed);
+
+       /* TODO: shut runtime down, release resources, etc. */
+       return test_failed;
+}