#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-time.h>
#include <mono/utils/mono-mmap.h>
+#include <mono/utils/json.h>
#include "mini.h"
#include "seq-points.h"
gboolean soft_debug;
gboolean log_generics;
gboolean log_instances;
+ gboolean gen_seq_points_file;
gboolean direct_pinvoke;
gboolean direct_icalls;
gboolean no_direct_calls;
gboolean print_skipped_methods;
gboolean stats;
char *tool_prefix;
+ char *ld_flags;
gboolean autoreg;
char *mtriple;
char *llvm_path;
char *instances_logfile_path;
char *logfile;
+ gboolean dump_json;
} MonoAotOptions;
typedef struct MonoAotStats {
/* ARCHITECTURE SPECIFIC CODE */
-#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_POWERPC)
+#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_POWERPC) || defined(TARGET_ARM64)
#define EMIT_DWARF_INFO 1
#endif
encode_value (container ? 1 : 0, p, &p);
if (container) {
encode_value (container->is_method, p, &p);
- g_assert (par->serial == 0);
+ g_assert (par->gshared_constraint == 0);
if (container->is_method)
encode_method_ref (acfg, container->owner.method, p, &p);
else
encode_klass_ref (acfg, container->owner.klass, p, &p);
} else {
- encode_value (par->serial, p, &p);
+ encode_value (par->gshared_constraint, p, &p);
}
} else if (klass->byval_arg.type == MONO_TYPE_PTR) {
encode_value (MONO_AOT_TYPEREF_PTR, p, &p);
* - it allows the setting of breakpoints of aot-ed methods.
*/
debug_sym = get_debug_sym (method, "", acfg->method_label_hash);
+ cfg->asm_debug_symbol = g_strdup (debug_sym);
if (acfg->need_no_dead_strip)
fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym);
emit_line (acfg);
if (acfg->aot_opts.write_symbols) {
- emit_symbol_size (acfg, debug_sym, ".");
+ if (debug_sym)
+ emit_symbol_size (acfg, debug_sym, ".");
+ else
+ emit_symbol_size (acfg, cfg->asm_symbol, ".");
g_free (debug_sym);
}
}
case MONO_PATCH_INFO_LDSTR_LIT: {
const char *s = patch_info->data.target;
+ int len = strlen (s);
- encode_value (strlen (s), p, &p);
- memcpy (p, s, strlen (s) + 1);
+ encode_value (len, p, &p);
+ memcpy (p, s, len + 1);
+ p += len + 1;
break;
}
default:
}
static void
-emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
+emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg, gboolean store_seq_points)
{
MonoMethod *method;
int i, k, buf_size, method_index;
}
seq_points = cfg->seq_point_info;
-
- seq_points_size = (cfg->gen_seq_points)? seq_point_info_get_write_size (seq_points) : 0;
+ seq_points_size = (store_seq_points)? seq_point_info_get_write_size (seq_points) : 0;
buf_size = header->num_clauses * 256 + debug_info_size + 2048 + seq_points_size + cfg->gc_map_size;
+
p = buf = g_malloc (buf_size);
use_unwind_ops = cfg->unwind_ops != NULL;
- flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0) | (header->num_clauses ? 4 : 0) | (seq_points ? 8 : 0) | (cfg->compile_llvm ? 16 : 0) | (jinfo->has_try_block_holes ? 32 : 0) | (cfg->gc_map ? 64 : 0) | (jinfo->has_arch_eh_info ? 128 : 0);
+ flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0) | (header->num_clauses ? 4 : 0) | (seq_points_size ? 8 : 0) | (cfg->compile_llvm ? 16 : 0) | (jinfo->has_try_block_holes ? 32 : 0) | (cfg->gc_map ? 64 : 0) | (jinfo->has_arch_eh_info ? 128 : 0);
encode_value (flags, p, &p);
}
}
- if (seq_points)
+ if (seq_points_size)
p += seq_point_info_write (seq_points, p);
g_assert (debug_info_size < buf_size);
if (acfg->llvm && !acfg->thumb_mixed) {
emit_label (acfg, plt_entry->llvm_symbol);
if (acfg->llvm_separate) {
- emit_global (acfg, plt_entry->llvm_symbol, TRUE);
+ emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE);
+#if defined(TARGET_MACH)
fprintf (acfg->fp, ".private_extern %s\n", plt_entry->llvm_symbol);
+#endif
}
}
emit_label (acfg, plt_entry->llvm_symbol);
+ if (acfg->llvm_separate)
+ emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE);
+
arch_emit_llvm_plt_entry (acfg, i);
if (debug_sym) {
opts->autoreg = TRUE;
} else if (str_begins_with (arg, "tool-prefix=")) {
opts->tool_prefix = g_strdup (arg + strlen ("tool-prefix="));
+ } else if (str_begins_with (arg, "ld-flags=")) {
+ opts->ld_flags = g_strdup (arg + strlen ("ld-flags="));
} else if (str_begins_with (arg, "soft-debug")) {
opts->soft_debug = TRUE;
+ } else if (str_begins_with (arg, "gen-seq-points-file")) {
+ opts->gen_seq_points_file = TRUE;
} else if (str_begins_with (arg, "direct-pinvoke")) {
opts->direct_pinvoke = TRUE;
} else if (str_begins_with (arg, "direct-icalls")) {
exit (0);
} else if (str_begins_with (arg, "gc-maps")) {
mini_gc_enable_gc_maps_for_aot ();
+ } else if (str_begins_with (arg, "dump")) {
+ opts->dump_json = TRUE;
} else if (str_begins_with (arg, "help") || str_begins_with (arg, "?")) {
printf ("Supported options for --aot:\n");
printf (" outfile=\n");
printf (" tool-prefix=\n");
printf (" readonly-value=\n");
printf (" soft-debug\n");
+ printf (" gen-seq-points-file\n");
printf (" gc-maps\n");
printf (" print-skipped\n");
printf (" no-instances\n");
printf (" stats\n");
+ printf (" dump\n");
printf (" info\n");
printf (" help/?\n");
exit (0);
}
add_generic_class_with_depth (acfg, m->klass, depth + 5, "method");
}
- if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED && !strcmp (m->name, "ElementAddr"))
- add_extra_method_with_depth (acfg, m, depth + 1);
+ if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (m);
+
+ if (info && info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR)
+ add_extra_method_with_depth (acfg, m, depth + 1);
+ }
break;
}
case MONO_PATCH_INFO_VTABLE: {
return res;
}
+static int
+execute_system (const char * command)
+{
+ int status;
+
+#if _WIN32
+ int size = MultiByteToWideChar (CP_UTF8, 0 , command , -1, NULL , 0);
+ wchar_t* wstr = g_malloc (sizeof (wchar_t) * size);
+ MultiByteToWideChar (CP_UTF8, 0, command, -1, wstr , size);
+ status = _wsystem (wstr);
+ g_free (wstr);
+#else
+ status = system (command);
+#endif
+
+ return status;
+}
+
#ifdef ENABLE_LLVM
/*
* return OverwriteComplete;
* Here, if 'Earlier' refers to a memset, and Later has no size info, it mistakenly thinks the memset is redundant.
*/
- opts = g_strdup ("-targetlibinfo -no-aa -basicaa -notti -instcombine -simplifycfg -sroa -domtree -early-cse -lazy-value-info -correlated-propagation -simplifycfg -instcombine -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop-idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt -sccp -instcombine -lazy-value-info -correlated-propagation -domtree -memdep -adce -simplifycfg -instcombine -strip-dead-prototypes -domtree -verify");
+ opts = g_strdup ("-targetlibinfo -no-aa -basicaa -notti -instcombine -simplifycfg -inline-cost -inline -sroa -domtree -early-cse -lazy-value-info -correlated-propagation -simplifycfg -instcombine -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop-idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt -sccp -instcombine -lazy-value-info -correlated-propagation -domtree -memdep -adce -simplifycfg -instcombine -strip-dead-prototypes -domtree -verify");
#if 1
command = g_strdup_printf ("%sopt -f %s -o \"%s.opt.bc\" \"%s.bc\"", acfg->aot_opts.llvm_path, opts, acfg->tmpbasename, acfg->tmpbasename);
aot_printf (acfg, "Executing opt: %s\n", command);
- if (system (command) != 0)
+ if (execute_system (command) != 0)
return FALSE;
#endif
g_free (opts);
aot_printf (acfg, "Executing llc: %s\n", command);
- if (system (command) != 0)
+ if (execute_system (command) != 0)
return FALSE;
return TRUE;
}
emit_method_code (acfg, cfg);
}
- sprintf (symbol, "methods_end");
emit_section_change (acfg, ".text", 0);
emit_alignment_code (acfg, 8);
- emit_label (acfg, symbol);
-
emit_label (acfg, "jit_code_end");
/* To distinguish it from the next symbol */
#endif
}
}
+ emit_int32 (acfg, 0);
}
static void
int i;
char symbol [256];
gint32 *offsets;
+ SeqPointData sp_data;
+ gboolean seq_points_to_file = FALSE;
offsets = g_new0 (gint32, acfg->nmethods);
for (i = 0; i < acfg->nmethods; ++i) {
if (acfg->cfgs [i]) {
- emit_exception_debug_info (acfg, acfg->cfgs [i]);
- offsets [i] = acfg->cfgs [i]->ex_info_offset;
+ MonoCompile *cfg = acfg->cfgs [i];
+
+ // By design aot-runtime decode_exception_debug_info is not able to load sequence point debug data from a file.
+ // As it is not possible to load debug data from a file its is also not possible to store it in a file.
+ gboolean method_seq_points_to_file = acfg->aot_opts.gen_seq_points_file &&
+ cfg->gen_seq_points && !cfg->gen_seq_points_debug_data;
+ gboolean method_seq_points_to_binary = cfg->gen_seq_points && !method_seq_points_to_file;
+
+ emit_exception_debug_info (acfg, cfg, method_seq_points_to_binary);
+ offsets [i] = cfg->ex_info_offset;
+
+ if (method_seq_points_to_file) {
+ if (!seq_points_to_file) {
+ seq_point_data_init (&sp_data, acfg->nmethods);
+ seq_points_to_file = TRUE;
+ }
+ seq_point_data_add (&sp_data, cfg->method->token, cfg->seq_point_info);
+ }
} else {
offsets [i] = 0;
}
}
+ if (seq_points_to_file) {
+ char *seq_points_aot_file;
+ mono_image_get_aot_seq_point_path (acfg->image, &seq_points_aot_file);
+ seq_point_data_write (&sp_data, seq_points_aot_file);
+ seq_point_data_free (&sp_data);
+ g_free (seq_points_aot_file);
+ }
+
sprintf (symbol, "ex_info_offsets");
emit_section_change (acfg, RODATA_SECT, 1);
emit_alignment (acfg, 8);
emit_got_info (MonoAotCompile *acfg, gboolean llvm)
{
char symbol [256];
- int i, first_plt_got_patch, buf_size;
+ int i, first_plt_got_patch = 0, buf_size;
guint8 *p, *buf;
guint32 *got_info_offsets;
GotInfo *info = llvm ? &acfg->llvm_got_info : &acfg->got_info;
emit_pointer (acfg, "llvm_got_info_offsets");
else
emit_pointer (acfg, NULL);
- emit_pointer (acfg, "methods_end");
emit_pointer (acfg, "unwind_info");
emit_pointer (acfg, "mem_end");
emit_pointer (acfg, "image_table");
sprintf (symbol2, "%sme_%x", acfg->temp_prefix, i);
- mono_dwarf_writer_emit_method (acfg->dwarf, cfg, cfg->method, cfg->asm_symbol, symbol2, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->d.method, mono_domain_get ()));
+ mono_dwarf_writer_emit_method (acfg->dwarf, cfg, cfg->method, cfg->asm_symbol, symbol2, cfg->asm_debug_symbol, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->d.method, mono_domain_get ()));
}
#endif
}
char *command, *objfile;
char *outfile_name, *tmp_outfile_name, *llvm_ofile;
const char *tool_prefix = acfg->aot_opts.tool_prefix ? acfg->aot_opts.tool_prefix : "";
+ char *ld_flags = acfg->aot_opts.ld_flags ? acfg->aot_opts.ld_flags : g_strdup("");
#if defined(TARGET_AMD64) && !defined(TARGET_MACH)
#define AS_OPTIONS "--64"
#define LD_NAME "gcc -dynamiclib"
#elif defined(TARGET_AMD64) && defined(TARGET_MACH)
#define LD_NAME "clang --shared"
-#elif defined(HOST_WIN32)
+#elif defined(TARGET_WIN32) && !defined(TARGET_ANDROID)
#define LD_NAME "gcc -shared --dll"
#elif defined(TARGET_X86) && defined(TARGET_MACH) && !defined(__native_client_codegen__)
#define LD_NAME "clang -m32 -dynamiclib"
}
command = g_strdup_printf ("%s%s %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS, acfg->as_args ? acfg->as_args->str : "", objfile, acfg->tmpfname);
aot_printf (acfg, "Executing the native assembler: %s\n", command);
- if (system (command) != 0) {
+ if (execute_system (command) != 0) {
g_free (command);
g_free (objfile);
return 1;
if (acfg->llvm_separate && !acfg->llvm_owriter) {
command = g_strdup_printf ("%s%s %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS, acfg->as_args ? acfg->as_args->str : "", acfg->llvm_ofile, acfg->llvm_sfile);
aot_printf (acfg, "Executing the native assembler: %s\n", command);
- if (system (command) != 0) {
+ if (execute_system (command) != 0) {
g_free (command);
g_free (objfile);
return 1;
llvm_ofile = g_strdup ("");
}
+ /* replace the ; flags separators with spaces */
+ g_strdelimit (ld_flags, ";", ' ');
+
#ifdef LD_NAME
- command = g_strdup_printf ("%s -o %s %s %s.o", LD_NAME, tmp_outfile_name, llvm_ofile, acfg->tmpfname);
+ command = g_strdup_printf ("%s -o %s %s %s.o %s", LD_NAME, tmp_outfile_name, llvm_ofile, acfg->tmpfname, ld_flags);
#else
- command = g_strdup_printf ("%sld %s -shared -o %s %s %s.o", tool_prefix, LD_OPTIONS, tmp_outfile_name, llvm_ofile, acfg->tmpfname);
+ command = g_strdup_printf ("%sld %s -shared -o %s %s %s.o %s", tool_prefix, LD_OPTIONS, tmp_outfile_name, llvm_ofile,
+ acfg->tmpfname, ld_flags);
#endif
aot_printf (acfg, "Executing the native linker: %s\n", command);
- if (system (command) != 0) {
+ if (execute_system (command) != 0) {
g_free (tmp_outfile_name);
g_free (outfile_name);
g_free (command);
g_free (objfile);
+ g_free (ld_flags);
return 1;
}
/*com = g_strdup_printf ("strip --strip-unneeded %s%s", acfg->image->name, MONO_SOLIB_EXT);
printf ("Stripping the binary: %s\n", com);
- system (com);
+ execute_system (com);
g_free (com);*/
#if defined(TARGET_ARM) && !defined(TARGET_MACH)
*/
command = g_strdup_printf ("%sstrip --strip-symbol=\\$a --strip-symbol=\\$d %s", tool_prefix, tmp_outfile_name);
aot_printf (acfg, "Stripping the binary: %s\n", command);
- if (system (command) != 0) {
+ if (execute_system (command) != 0) {
g_free (tmp_outfile_name);
g_free (outfile_name);
g_free (command);
#if defined(TARGET_MACH)
command = g_strdup_printf ("dsymutil %s", outfile_name);
aot_printf (acfg, "Executing dsymutil: %s\n", command);
- if (system (command) != 0) {
+ if (execute_system (command) != 0) {
return 1;
}
#endif
g_free (acfg);
}
+#define WRAPPER(e,n) n,
+static const char* const
+wrapper_type_names [MONO_WRAPPER_NUM + 1] = {
+#include "mono/metadata/wrapper-types.h"
+ NULL
+};
+
+static G_GNUC_UNUSED const char*
+get_wrapper_type_name (int type)
+{
+ return wrapper_type_names [type];
+}
+
+//#define DUMP_PLT
+//#define DUMP_GOT
+
+static void aot_dump (MonoAotCompile *acfg)
+{
+ FILE *dumpfile;
+ char * dumpname;
+
+ JsonWriter writer;
+ json_writer_init (&writer);
+
+ json_writer_object_begin(&writer);
+
+ // Methods
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "methods");
+ json_writer_array_begin (&writer);
+
+ int i;
+ for (i = 0; i < acfg->nmethods; ++i) {
+ MonoCompile *cfg;
+ MonoMethod *method;
+ MonoClass *klass;
+
+ cfg = acfg->cfgs [i];
+ if (!cfg)
+ continue;
+
+ method = cfg->orig_method;
+
+ json_writer_indent (&writer);
+ json_writer_object_begin(&writer);
+
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "name");
+ json_writer_printf (&writer, "\"%s\",\n", method->name);
+
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "signature");
+ json_writer_printf (&writer, "\"%s\",\n", mono_method_full_name (method,
+ /*signature=*/TRUE));
+
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "code_size");
+ json_writer_printf (&writer, "\"%d\",\n", cfg->code_size);
+
+ klass = method->klass;
+
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "class");
+ json_writer_printf (&writer, "\"%s\",\n", klass->name);
+
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "namespace");
+ json_writer_printf (&writer, "\"%s\",\n", klass->name_space);
+
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "wrapper_type");
+ json_writer_printf (&writer, "\"%s\",\n", get_wrapper_type_name(method->wrapper_type));
+
+ json_writer_indent_pop (&writer);
+ json_writer_indent (&writer);
+ json_writer_object_end (&writer);
+ json_writer_printf (&writer, ",\n");
+ }
+
+ json_writer_indent_pop (&writer);
+ json_writer_indent (&writer);
+ json_writer_array_end (&writer);
+ json_writer_printf (&writer, ",\n");
+
+ // PLT entries
+#ifdef DUMP_PLT
+ json_writer_indent_push (&writer);
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "plt");
+ json_writer_array_begin (&writer);
+
+ for (i = 0; i < acfg->plt_offset; ++i) {
+ MonoPltEntry *plt_entry = NULL;
+ MonoJumpInfo *ji;
+
+ if (i == 0)
+ /*
+ * The first plt entry is unused.
+ */
+ continue;
+
+ plt_entry = g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i));
+ ji = plt_entry->ji;
+
+ json_writer_indent (&writer);
+ json_writer_printf (&writer, "{ ");
+ json_writer_object_key(&writer, "symbol");
+ json_writer_printf (&writer, "\"%s\" },\n", plt_entry->symbol);
+ }
+
+ json_writer_indent_pop (&writer);
+ json_writer_indent (&writer);
+ json_writer_array_end (&writer);
+ json_writer_printf (&writer, ",\n");
+#endif
+
+ // GOT entries
+#ifdef DUMP_GOT
+ json_writer_indent_push (&writer);
+ json_writer_indent (&writer);
+ json_writer_object_key(&writer, "got");
+ json_writer_array_begin (&writer);
+
+ json_writer_indent_push (&writer);
+ for (i = 0; i < acfg->got_info.got_patches->len; ++i) {
+ MonoJumpInfo *ji = g_ptr_array_index (acfg->got_info.got_patches, i);
+
+ json_writer_indent (&writer);
+ json_writer_printf (&writer, "{ ");
+ json_writer_object_key(&writer, "patch_name");
+ json_writer_printf (&writer, "\"%s\" },\n", get_patch_name (ji->type));
+ }
+
+ json_writer_indent_pop (&writer);
+ json_writer_indent (&writer);
+ json_writer_array_end (&writer);
+ json_writer_printf (&writer, ",\n");
+#endif
+
+ json_writer_indent_pop (&writer);
+ json_writer_indent (&writer);
+ json_writer_object_end (&writer);
+
+ dumpname = g_strdup_printf ("%s.json", g_path_get_basename (acfg->image->name));
+ dumpfile = fopen (dumpname, "w+");
+ g_free (dumpname);
+
+ fprintf (dumpfile, "%s", writer.text->str);
+ fclose (dumpfile);
+
+ json_writer_destroy (&writer);
+}
+
int
mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options)
{
mono_set_partial_sharing_supported (TRUE);
*/
+ mono_set_partial_sharing_supported (FALSE);
+
res = collect_methods (acfg);
if (!res)
return 1;
cfg->asm_symbol = get_debug_sym (cfg->orig_method, "", acfg->method_label_hash);
else
cfg->asm_symbol = g_strdup_printf ("%s%sm_%x", acfg->temp_prefix, acfg->llvm_label_prefix, method_index);
+ cfg->asm_debug_symbol = cfg->asm_symbol;
}
}
aot_printf (acfg, "JIT time: %d ms, Generation time: %d ms, Assembly+Link time: %d ms.\n", acfg->stats.jit_time / 1000, acfg->stats.gen_time / 1000, acfg->stats.link_time / 1000);
+ if (acfg->aot_opts.dump_json)
+ aot_dump (acfg);
+
acfg_free (acfg);
return 0;