/buildver-sgen.h
/buildver-boehm.h
/regressiontests.out
+/test-mono-interp-whitebox
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
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
#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
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)
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)
g_free (local_ref_count);
}
+void
+mono_test_interp_cprop (TransformData *td)
+{
+ interp_cprop (td);
+}
+
static void
interp_super_instructions (TransformData *td)
{
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
--- /dev/null
+#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__ */
--- /dev/null
+.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
+ }
+}
--- /dev/null
+#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, ¤t, MINT_LDC_I4))
+ return 1;
+ if (READ32 (¤t->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;
+}