X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Faot-compiler.c;h=b5516fb8034973468e22a76047c504a9cda3e76a;hb=5600f484aa032bddfe71c1d85a80a33a32ae4a92;hp=2ca0017d20501e2b524f734372d6d033bf7c0b76;hpb=caea6842586ed0c7c793f5452065c8a4b5421d75;p=mono.git diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c index 2ca0017d205..b5516fb8034 100644 --- a/mono/mini/aot-compiler.c +++ b/mono/mini/aot-compiler.c @@ -80,7 +80,7 @@ #ifdef TARGET_WIN32 #define SHARED_EXT ".dll" -#elif defined(__ppc__) && defined(__MACH__) +#elif defined(__ppc__) && defined(__APPLE__) #define SHARED_EXT ".dylib" #elif defined(__APPLE__) && defined(TARGET_X86) && !defined(__native_client_codegen__) #define SHARED_EXT ".dylib" @@ -114,6 +114,7 @@ typedef struct MonoAotOptions { char *tool_prefix; gboolean autoreg; char *mtriple; + char *llvm_path; } MonoAotOptions; typedef struct MonoAotStats { @@ -143,7 +144,7 @@ typedef struct MonoAotCompile { GPtrArray *extra_methods; GPtrArray *image_table; GPtrArray *globals; - GList *method_order; + GPtrArray *method_order; guint32 *plt_got_info_offsets; guint32 got_offset, plt_offset, plt_got_offset_base; guint32 final_got_size; @@ -184,13 +185,14 @@ typedef struct MonoAotCompile { MonoClass **typespec_classes; GString *llc_args; GString *as_args; - gboolean thumb_mixed; + gboolean thumb_mixed, need_no_dead_strip, need_pt_gnu_stack; } MonoAotCompile; typedef struct { int plt_offset; - char *symbol, *llvm_symbol; + char *symbol, *llvm_symbol, *debug_sym; MonoJumpInfo *ji; + gboolean jit_used, llvm_used; } MonoPltEntry; #define mono_acfg_lock(acfg) EnterCriticalSection (&((acfg)->mutex)) @@ -372,7 +374,10 @@ static void emit_string_symbol (MonoAotCompile *acfg, const char *name, const char *value) { img_writer_emit_section_change (acfg->w, RODATA_SECT, 1); - emit_global (acfg, name, FALSE); +#ifdef __APPLE__ + /* On apple, all symbols need to be aligned to avoid warnings from ld */ + emit_alignment (acfg, 4); +#endif img_writer_emit_label (acfg->w, name); img_writer_emit_string (acfg->w, value); } @@ -475,7 +480,7 @@ encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf) #else #define AOT_FUNC_ALIGNMENT 16 #endif -#if defined(TARGET_X86) && defined(__native_client_codegen__) +#if (defined(TARGET_X86) || defined(TARGET_AMD64)) && defined(__native_client_codegen__) #undef AOT_FUNC_ALIGNMENT #define AOT_FUNC_ALIGNMENT 32 #endif @@ -488,6 +493,50 @@ encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf) #define PPC_LDX_OP "lwzx" #endif +#ifdef TARGET_AMD64 +#define AOT_TARGET_STR "AMD64" +#endif + +#ifdef TARGET_ARM +#ifdef __MACH__ +#define AOT_TARGET_STR "ARM (MACH)" +#else +#define AOT_TARGET_STR "ARM (!MACH)" +#endif +#endif + +#ifdef TARGET_POWERPC64 +#ifdef __mono_ilp32__ +#define AOT_TARGET_STR "POWERPC64 (mono ilp32)" +#else +#define AOT_TARGET_STR "POWERPC64 (!mono ilp32)" +#endif +#else +#ifdef TARGET_POWERPC +#ifdef __mono_ilp32__ +#define AOT_TARGET_STR "POWERPC (mono ilp32)" +#else +#define AOT_TARGET_STR "POWERPC (!mono ilp32)" +#endif +#endif +#endif + +#ifdef TARGET_WIN32 +#define AOT_TARGET_STR "WIN32" +#endif + +#ifdef TARGET_X86 +#ifdef __native_client_codegen__ +#define AOT_TARGET_STR "X86 (native client codegen)" +#else +#define AOT_TARGET_STR "X86 (!native client codegen)" +#endif +#endif + +#ifndef AOT_TARGET_STR +#define AOT_TARGET_STR "" +#endif + static void arch_init (MonoAotCompile *acfg) { @@ -505,7 +554,6 @@ arch_init (MonoAotCompile *acfg) #ifdef TARGET_ARM if (acfg->aot_opts.mtriple && strstr (acfg->aot_opts.mtriple, "darwin")) { g_string_append (acfg->llc_args, "-mattr=+v6"); - acfg->llvm_label_prefix = "_"; } else { #ifdef ARM_FPU_VFP g_string_append (acfg->llc_args, " -mattr=+vfp2,+d16"); @@ -520,6 +568,15 @@ arch_init (MonoAotCompile *acfg) if (acfg->aot_opts.mtriple) mono_arch_set_target (acfg->aot_opts.mtriple); #endif + +#ifdef __APPLE__ + acfg->llvm_label_prefix = "_"; + acfg->need_no_dead_strip = TRUE; +#endif + +#if defined(__linux__) && !defined(TARGET_ARM) + acfg->need_pt_gnu_stack = TRUE; +#endif } /* @@ -689,8 +746,14 @@ arch_emit_plt_entry (MonoAotCompile *acfg, int index) { #if defined(TARGET_X86) guint32 offset = (acfg->plt_got_offset_base + index) * sizeof (gpointer); - -#ifdef __native_client_codegen__ +#if defined(__default_codegen__) + /* jmp *(%ebx) */ + emit_byte (acfg, 0xff); + emit_byte (acfg, 0xa3); + emit_int32 (acfg, offset); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); +#elif defined(__native_client_codegen__) const guint8 kSizeOfNaClJmp = 11; guint8 bytes[kSizeOfNaClJmp]; guint8 *pbytes = &bytes[0]; @@ -702,15 +765,9 @@ arch_emit_plt_entry (MonoAotCompile *acfg, int index) emit_byte (acfg, 0x68); /* hide data in a push */ emit_int32 (acfg, acfg->plt_got_info_offsets [index]); emit_alignment (acfg, AOT_FUNC_ALIGNMENT); -#else - /* jmp *(%ebx) */ - emit_byte (acfg, 0xff); - emit_byte (acfg, 0xa3); - emit_int32 (acfg, offset); - /* Used by mono_aot_get_plt_info_offset */ - emit_int32 (acfg, acfg->plt_got_info_offsets [index]); -#endif /* __native_client_codegen__ */ +#endif /*__native_client_codegen__*/ #elif defined(TARGET_AMD64) +#if defined(__default_codegen__) /* * We can't emit jumps because they are 32 bits only so they can't be patched. * So we make indirect calls through GOT entries which are patched by the AOT @@ -722,6 +779,27 @@ arch_emit_plt_entry (MonoAotCompile *acfg, int index) emit_symbol_diff (acfg, acfg->got_symbol, ".", ((acfg->plt_got_offset_base + index) * sizeof (gpointer)) -4); /* Used by mono_aot_get_plt_info_offset */ emit_int32 (acfg, acfg->plt_got_info_offsets [index]); +#elif defined(__native_client_codegen__) + guint8 buf [256]; + guint8 *buf_aligned = ALIGN_TO(buf, kNaClAlignment); + guint8 *code = buf_aligned; + + /* mov (%rip), %r11d */ + emit_byte (acfg, '\x45'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x1d'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", ((acfg->plt_got_offset_base + index) * sizeof (gpointer)) -4); + + amd64_jump_reg (code, AMD64_R11); + /* This should be constant for the plt patch */ + g_assert ((size_t)(code-buf_aligned) == 10); + emit_bytes (acfg, buf_aligned, code - buf_aligned); + + /* Hide data in a push imm32 so it passes validation */ + emit_byte (acfg, 0x68); /* push */ + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); + emit_alignment (acfg, AOT_FUNC_ALIGNMENT); +#endif /*__native_client_codegen__*/ #elif defined(TARGET_ARM) guint8 buf [256]; guint8 *code; @@ -755,6 +833,33 @@ arch_emit_plt_entry (MonoAotCompile *acfg, int index) #endif } +static void +arch_emit_llvm_plt_entry (MonoAotCompile *acfg, int index) +{ +#if defined(TARGET_ARM) +#if 0 + /* LLVM calls the PLT entries using bl, so emit a stub */ + /* FIXME: Too much overhead on every call */ + fprintf (acfg->fp, ".thumb_func\n"); + fprintf (acfg->fp, "bx pc\n"); + fprintf (acfg->fp, "nop\n"); + fprintf (acfg->fp, ".arm\n"); +#endif + /* LLVM calls the PLT entries using bl, so these have to be thumb2 */ + /* The caller already transitioned to thumb */ + /* The code below should be 12 bytes long */ + fprintf (acfg->fp, "ldr ip, [pc, #8]\n"); + /* thumb can't encode ld pc, [pc, ip] */ + fprintf (acfg->fp, "add ip, pc, ip\n"); + fprintf (acfg->fp, "ldr ip, [ip, #0]\n"); + fprintf (acfg->fp, "bx ip\n"); + emit_symbol_diff (acfg, acfg->got_symbol, ".", ((acfg->plt_got_offset_base + index) * sizeof (gpointer)) + 4); + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); +#else + g_assert_not_reached (); +#endif +} + /* * arch_emit_specific_trampoline: * @@ -778,6 +883,7 @@ arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size * - all the trampolines should be of the same length. */ #if defined(TARGET_AMD64) +#if defined(__default_codegen__) /* This should be exactly 16 bytes long */ *tramp_size = 16; /* call *(%rip) */ @@ -786,8 +892,61 @@ arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size emit_byte (acfg, '\x15'); emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4); /* This should be relative to the start of the trampoline */ - emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4 + 19); + emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset+1) * sizeof (gpointer)) + 7); emit_zero_bytes (acfg, 5); +#elif defined(__native_client_codegen__) + guint8 buf [256]; + guint8 *buf_aligned = ALIGN_TO(buf, kNaClAlignment); + guint8 *code = buf_aligned; + guint8 *call_start; + size_t call_len; + int got_offset; + + /* Emit this call in 'code' so we can find out how long it is. */ + amd64_call_reg (code, AMD64_R11); + call_start = mono_arch_nacl_skip_nops (buf_aligned); + call_len = code - call_start; + + /* The tramp_size is twice the NaCl alignment because it starts with */ + /* a call which needs to be aligned to the end of the boundary. */ + *tramp_size = kNaClAlignment*2; + { + /* Emit nops to align call site below which is 7 bytes plus */ + /* the length of the call sequence emitted above. */ + /* Note: this requires the specific trampoline starts on a */ + /* kNaclAlignedment aligned address, which it does because */ + /* it's its own function that is aligned. */ + guint8 nop_buf[256]; + guint8 *nopbuf_aligned = ALIGN_TO (nop_buf, kNaClAlignment); + guint8 *nopbuf_end = mono_arch_nacl_pad (nopbuf_aligned, kNaClAlignment - 7 - (call_len)); + emit_bytes (acfg, nopbuf_aligned, nopbuf_end - nopbuf_aligned); + } + /* The trampoline is stored at the offset'th pointer, the -4 is */ + /* present because RIP relative addressing starts at the end of */ + /* the current instruction, while the label "." is relative to */ + /* the beginning of the current asm location, which in this case */ + /* is not the mov instruction, but the offset itself, due to the */ + /* way the bytes and ints are emitted here. */ + got_offset = (offset * sizeof(gpointer)) - 4; + + /* mov (%rip), %r11d */ + emit_byte (acfg, '\x45'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x1d'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", got_offset); + + /* naclcall %r11 */ + emit_bytes (acfg, call_start, call_len); + + /* The arg is stored at the offset+1 pointer, relative to beginning */ + /* of trampoline: 7 for mov, plus the call length, and 1 for push. */ + got_offset = ((offset + 1) * sizeof(gpointer)) + 7 + call_len + 1; + + /* We can't emit this data directly, hide in a "push imm32" */ + emit_byte (acfg, '\x68'); /* push */ + emit_symbol_diff (acfg, acfg->got_symbol, ".", got_offset); + emit_alignment (acfg, kNaClAlignment); +#endif /*__native_client_codegen__*/ #elif defined(TARGET_ARM) guint8 buf [128]; guint8 *code; @@ -897,7 +1056,7 @@ arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size * CALL_TARGET is the symbol pointing to the native code of METHOD. */ static void -arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, const char *call_target) +arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, const char *call_target) { #if defined(TARGET_AMD64) guint8 buf [32]; @@ -930,6 +1089,13 @@ arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, const char guint8 buf [128]; guint8 *code; + if (acfg->thumb_mixed && cfg->compile_llvm) { + fprintf (acfg->fp, "add r0, r0, #%d\n", sizeof (MonoObject)); + fprintf (acfg->fp, "b %s\n", call_target); + fprintf (acfg->fp, ".arm\n"); + return; + } + code = buf; ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject)); @@ -946,7 +1112,10 @@ arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, const char img_writer_emit_reloc (acfg->w, R_ARM_JUMP24, call_target, -8); emit_bytes (acfg, buf, 4); } else { - fprintf (acfg->fp, "\n\tb %s\n", call_target); + if (acfg->thumb_mixed && cfg->compile_llvm) + fprintf (acfg->fp, "\n\tbx %s\n", call_target); + else + fprintf (acfg->fp, "\n\tb %s\n", call_target); } #elif defined(TARGET_POWERPC) int this_pos = 3; @@ -974,6 +1143,7 @@ static void arch_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) { #if defined(TARGET_AMD64) +#if defined(__default_codegen__) /* This should be exactly 13 bytes long */ *tramp_size = 13; @@ -987,6 +1157,31 @@ arch_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_ emit_byte (acfg, '\xff'); emit_byte (acfg, '\x25'); emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (gpointer)) - 4); +#elif defined(__native_client_codegen__) + guint8 buf [128]; + guint8 *buf_aligned = ALIGN_TO(buf, kNaClAlignment); + guint8 *code = buf_aligned; + + /* mov (%rip), %r10d */ + emit_byte (acfg, '\x45'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x15'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4); + + /* mov (%rip), %r11d */ + emit_byte (acfg, '\x45'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x1d'); + emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (gpointer)) - 4); + + /* nacljmp *%r11 */ + amd64_jump_reg (code, AMD64_R11); + emit_bytes (acfg, buf_aligned, code - buf_aligned); + + emit_alignment (acfg, kNaClAlignment); + *tramp_size = kNaClAlignment; +#endif /*__native_client_codegen__*/ + #elif defined(TARGET_ARM) guint8 buf [128]; guint8 *code; @@ -1096,50 +1291,74 @@ arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size) { #if defined(TARGET_AMD64) guint8 *buf, *code; +#if defined(__native_client_codegen__) + guint8 *buf_alloc; +#endif guint8 *labels [3]; + guint8 mov_buf[3]; + guint8 *mov_buf_ptr = mov_buf; + const int kSizeOfMove = 7; +#if defined(__default_codegen__) code = buf = g_malloc (256); +#elif defined(__native_client_codegen__) + buf_alloc = g_malloc (256 + kNaClAlignment + kSizeOfMove); + buf = ((guint)buf_alloc + kNaClAlignment) & ~kNaClAlignmentMask; + /* The RIP relative move below is emitted first */ + buf += kSizeOfMove; + code = buf; +#endif /* FIXME: Optimize this, i.e. use binary search etc. */ /* Maybe move the body into a separate function (slower, but much smaller) */ - /* R11 is a free register */ + /* MONO_ARCH_IMT_SCRATCH_REG is a free register */ labels [0] = code; - amd64_alu_membase_imm (code, X86_CMP, AMD64_R11, 0, 0); + amd64_alu_membase_imm (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, 0); labels [1] = code; - amd64_branch8 (code, X86_CC_Z, FALSE, 0); + amd64_branch8 (code, X86_CC_Z, 0, FALSE); /* Check key */ - amd64_alu_membase_reg (code, X86_CMP, AMD64_R11, 0, MONO_ARCH_IMT_REG); + amd64_alu_membase_reg_size (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, MONO_ARCH_IMT_REG, sizeof (gpointer)); labels [2] = code; - amd64_branch8 (code, X86_CC_Z, FALSE, 0); + amd64_branch8 (code, X86_CC_Z, 0, FALSE); /* Loop footer */ - amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, 2 * sizeof (gpointer)); + amd64_alu_reg_imm (code, X86_ADD, MONO_ARCH_IMT_SCRATCH_REG, 2 * sizeof (gpointer)); amd64_jump_code (code, labels [0]); /* Match */ mono_amd64_patch (labels [2], code); - amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, sizeof (gpointer), 8); - amd64_jump_membase (code, AMD64_R11, 0); + amd64_mov_reg_membase (code, MONO_ARCH_IMT_SCRATCH_REG, MONO_ARCH_IMT_SCRATCH_REG, sizeof (gpointer), sizeof (gpointer)); + amd64_jump_membase (code, MONO_ARCH_IMT_SCRATCH_REG, 0); /* No match */ /* FIXME: */ mono_amd64_patch (labels [1], code); x86_breakpoint (code); - amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 12345678, 8); - - /* mov (%rip), %r11 */ - emit_byte (acfg, '\x4d'); - emit_byte (acfg, '\x8b'); - emit_byte (acfg, '\x1d'); + /* mov (%rip), MONO_ARCH_IMT_SCRATCH_REG */ + amd64_emit_rex (mov_buf_ptr, sizeof(gpointer), MONO_ARCH_IMT_SCRATCH_REG, 0, AMD64_RIP); + *(mov_buf_ptr)++ = (unsigned char)0x8b; /* mov opcode */ + x86_address_byte (mov_buf_ptr, 0, MONO_ARCH_IMT_SCRATCH_REG & 0x7, 5); + emit_bytes (acfg, mov_buf, mov_buf_ptr - mov_buf); emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4); emit_bytes (acfg, buf, code - buf); - *tramp_size = code - buf + 7; + *tramp_size = code - buf + kSizeOfMove; +#if defined(__native_client_codegen__) + /* The tramp will be padded to the next kNaClAlignment bundle. */ + *tramp_size = ALIGN_TO ((*tramp_size), kNaClAlignment); +#endif + +#if defined(__default_codegen__) + g_free (buf); +#elif defined(__native_client_codegen__) + g_free (buf_alloc); +#endif + #elif defined(TARGET_X86) guint8 *buf, *code; #ifdef __native_client_codegen__ @@ -1147,11 +1366,11 @@ arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size) #endif guint8 *labels [3]; -#ifdef __native_client_codegen__ +#if defined(__default_codegen__) + code = buf = g_malloc (256); +#elif defined(__native_client_codegen__) buf_alloc = g_malloc (256 + kNaClAlignment); code = buf = ((guint)buf_alloc + kNaClAlignment) & ~kNaClAlignmentMask; -#else - code = buf = g_malloc (256); #endif /* Allocate a temporary stack slot */ @@ -1204,6 +1423,13 @@ arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size) emit_bytes (acfg, buf, code - buf); *tramp_size = code - buf; + +#if defined(__default_codegen__) + g_free (buf); +#elif defined(__native_client_codegen__) + g_free (buf_alloc); +#endif + #elif defined(TARGET_ARM) guint8 buf [128]; guint8 *code, *code2, *labels [16]; @@ -1858,6 +2084,15 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 g_assert_not_reached (); } break; + case MONO_WRAPPER_CASTCLASS: + if (!strcmp (method->name, "__castclass_with_cache")) { + encode_value (MONO_AOT_WRAPPER_CASTCLASS_WITH_CACHE, p, &p); + } else if (!strcmp (method->name, "__isinst_with_cache")) { + encode_value (MONO_AOT_WRAPPER_ISINST_WITH_CACHE, p, &p); + } else { + g_assert_not_reached (); + } + break; default: g_assert_not_reached (); } @@ -2012,7 +2247,7 @@ is_plt_patch (MonoJumpInfo *patch_info) static char* get_plt_symbol (MonoAotCompile *acfg, int plt_offset, MonoJumpInfo *patch_info) { -#ifdef __MACH__ +#ifdef __APPLE__ /* * The Apple linker reorganizes object files, so it doesn't like branches to local * labels, since those have no relocations. @@ -2139,8 +2374,7 @@ add_method_full (MonoAotCompile *acfg, MonoMethod *method, gboolean extra, int d index = acfg->method_index; add_method_with_index (acfg, method, index, extra); - /* FIXME: Fix quadratic behavior */ - acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (index)); + g_ptr_array_add (acfg->method_order, GUINT_TO_POINTER (index)); g_hash_table_insert (acfg->method_depth, method, GUINT_TO_POINTER (depth)); @@ -2431,7 +2665,14 @@ add_wrappers (MonoAotCompile *acfg) klass = mono_class_get (acfg->image, token); if (klass) add_method (acfg, mono_marshal_get_virtual_stelemref (mono_array_class_get (klass, 1))); + else + mono_loader_clear_error (); } + + /* castclass_with_check wrapper */ + add_method (acfg, mono_marshal_get_castclass_with_cache ()); + /* isinst_with_check wrapper */ + add_method (acfg, mono_marshal_get_isinst_with_cache ()); } /* @@ -2464,6 +2705,11 @@ add_wrappers (MonoAotCompile *acfg) token = MONO_TOKEN_TYPE_DEF | (i + 1); klass = mono_class_get (acfg->image, token); + if (!klass) { + mono_loader_clear_error (); + continue; + } + if (klass->delegate && klass != mono_defaults.delegate_class && klass != mono_defaults.multicastdelegate_class && !klass->generic_container) { method = mono_get_delegate_invoke (klass); @@ -2571,6 +2817,11 @@ add_wrappers (MonoAotCompile *acfg) token = MONO_TOKEN_TYPE_DEF | (i + 1); klass = mono_class_get (acfg->image, token); + if (!klass) { + mono_loader_clear_error (); + continue; + } + if (klass->valuetype && !klass->generic_container && can_marshal_struct (klass)) { add_method (acfg, mono_marshal_get_struct_to_ptr (klass)); add_method (acfg, mono_marshal_get_ptr_to_struct (klass)); @@ -2629,6 +2880,34 @@ add_generic_class (MonoAotCompile *acfg, MonoClass *klass, gboolean force) add_generic_class_with_depth (acfg, klass, 0); } +static gboolean +check_type_depth (MonoType *t, int depth) +{ + int i; + + if (depth > 8) + return TRUE; + + switch (t->type) { + case MONO_TYPE_GENERICINST: { + MonoGenericClass *gklass = t->data.generic_class; + MonoGenericInst *ginst = gklass->context.class_inst; + + if (ginst) { + for (i = 0; i < ginst->type_argc; ++i) { + if (check_type_depth (ginst->type_argv [i], depth + 1)) + return TRUE; + } + } + break; + } + default: + break; + } + + return FALSE; +} + /* * add_generic_class: * @@ -2651,6 +2930,9 @@ add_generic_class_with_depth (MonoAotCompile *acfg, MonoClass *klass, int depth) if (!klass->generic_class && !klass->rank) return; + if (check_type_depth (&klass->byval_arg, 0)) + return; + iter = NULL; while ((method = mono_class_get_methods (klass, &iter))) { if (mono_method_is_generic_sharable_impl_full (method, FALSE, FALSE)) @@ -2665,7 +2947,7 @@ add_generic_class_with_depth (MonoAotCompile *acfg, MonoClass *klass, int depth) * FIXME: Instances which are referenced by these methods are not added, * for example Array.Resize for List.Add (). */ - add_extra_method_with_depth (acfg, method, depth); + add_extra_method_with_depth (acfg, method, depth + 1); } if (klass->delegate) { @@ -2899,24 +3181,28 @@ add_generic_instances (MonoAotCompile *acfg) token = MONO_TOKEN_TYPE_SPEC | (i + 1); klass = mono_class_get (acfg->image, token); - if (!klass || klass->rank) + if (!klass || klass->rank) { + mono_loader_clear_error (); continue; + } add_generic_class (acfg, klass, FALSE); } /* Add types of args/locals */ for (i = 0; i < acfg->methods->len; ++i) { - int j; + int j, depth; method = g_ptr_array_index (acfg->methods, i); + depth = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_depth, method)); + sig = mono_method_signature (method); if (sig) { for (j = 0; j < sig->param_count; ++j) if (sig->params [j]->type == MONO_TYPE_GENERICINST) - add_generic_class (acfg, mono_class_from_mono_type (sig->params [j]), FALSE); + add_generic_class_with_depth (acfg, mono_class_from_mono_type (sig->params [j]), depth + 1); } header = mono_method_get_header (method); @@ -2924,7 +3210,7 @@ add_generic_instances (MonoAotCompile *acfg) if (header) { for (j = 0; j < header->num_locals; ++j) if (header->locals [j]->type == MONO_TYPE_GENERICINST) - add_generic_class (acfg, mono_class_from_mono_type (header->locals [j]), FALSE); + add_generic_class_with_depth (acfg, mono_class_from_mono_type (header->locals [j]), depth + 1); } } @@ -2985,6 +3271,24 @@ add_generic_instances (MonoAotCompile *acfg) add_extra_method (acfg, mono_marshal_get_native_wrapper (mono_class_inflate_generic_method (get_method, &ctx), TRUE, TRUE)); } } + + /* Same for CompareExchange */ + { + MonoGenericContext ctx; + MonoType *args [16]; + MonoMethod *cas_method; + MonoClass *interlocked_klass = mono_class_from_name (mono_defaults.corlib, "System.Threading", "Interlocked"); + gpointer iter = NULL; + + while ((cas_method = mono_class_get_methods (interlocked_klass, &iter))) { + if (!strcmp (cas_method->name, "CompareExchange") && cas_method->is_generic) { + memset (&ctx, 0, sizeof (ctx)); + args [0] = &mono_defaults.object_class->byval_arg; + ctx.method_inst = mono_metadata_get_generic_inst (1, args); + add_extra_method (acfg, mono_marshal_get_native_wrapper (mono_class_inflate_generic_method (cas_method, &ctx), TRUE, TRUE)); + } + } + } } } @@ -3103,6 +3407,7 @@ emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, gui /* Nullify the patch */ patch_info->type = MONO_PATCH_INFO_NONE; + plt_entry->jit_used = TRUE; } } @@ -3233,6 +3538,8 @@ emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg) debug_sym = get_debug_sym (method, "", acfg->method_label_hash); sprintf (symbol, "%sme_%x", acfg->temp_prefix, method_index); + if (acfg->need_no_dead_strip) + fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym); emit_local_symbol (acfg, debug_sym, symbol, TRUE); emit_label (acfg, debug_sym); } @@ -3275,6 +3582,7 @@ encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint break; case MONO_PATCH_INFO_MSCORLIB_GOT_ADDR: case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR: + case MONO_PATCH_INFO_CASTCLASS_CACHE: break; case MONO_PATCH_INFO_METHOD_REL: encode_value ((gint)patch_info->data.offset, p, &p); @@ -3413,7 +3721,6 @@ emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg) MonoMethod *method; GList *l; int pindex, buf_size, n_patches; - guint8 *code; GPtrArray *patches; MonoJumpInfo *patch_info; MonoMethodHeader *header; @@ -3422,7 +3729,6 @@ emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg) guint32 first_got_offset; method = cfg->orig_method; - code = cfg->native_code; header = mono_method_get_header (method); method_index = get_method_index (acfg, method); @@ -3566,7 +3872,7 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg) seq_points = cfg->seq_point_info; - buf_size = header->num_clauses * 256 + debug_info_size + 1024 + (seq_points ? (seq_points->len * 64) : 0) + cfg->gc_map_size; + buf_size = header->num_clauses * 256 + debug_info_size + 2048 + (seq_points ? (seq_points->len * 64) : 0) + cfg->gc_map_size; p = buf = g_malloc (buf_size); #ifdef MONO_ARCH_HAVE_XP_UNWIND @@ -3720,7 +4026,7 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg) if (cfg->gc_map) { encode_value (cfg->gc_map_size, p, &p); /* The GC map requires 4 bytes of alignment */ - while ((guint64)p % 4) + while ((gsize)p % 4) p ++; memcpy (p, cfg->gc_map, cfg->gc_map_size); p += cfg->gc_map_size; @@ -3746,6 +4052,8 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token) gpointer iter = NULL; if (!klass) { + mono_loader_clear_error (); + buf_size = 16; p = buf = g_malloc (buf_size); @@ -3822,14 +4130,49 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token) return res; } +static char* +get_plt_entry_debug_sym (MonoAotCompile *acfg, MonoJumpInfo *ji, GHashTable *cache) +{ + char *debug_sym = NULL; + + switch (ji->type) { + case MONO_PATCH_INFO_METHOD: + debug_sym = get_debug_sym (ji->data.method, "plt_", cache); + break; + case MONO_PATCH_INFO_INTERNAL_METHOD: + debug_sym = g_strdup_printf ("plt__jit_icall_%s", ji->data.name); + break; + case MONO_PATCH_INFO_CLASS_INIT: + debug_sym = g_strdup_printf ("plt__class_init_%s", mono_type_get_name (&ji->data.klass->byval_arg)); + sanitize_symbol (debug_sym); + break; + case MONO_PATCH_INFO_RGCTX_FETCH: + debug_sym = g_strdup_printf ("plt__rgctx_fetch_%d", acfg->label_generator ++); + break; + case MONO_PATCH_INFO_ICALL_ADDR: { + char *s = get_debug_sym (ji->data.method, "", cache); + + debug_sym = g_strdup_printf ("plt__icall_native_%s", s); + g_free (s); + break; + } + case MONO_PATCH_INFO_JIT_ICALL_ADDR: + debug_sym = g_strdup_printf ("plt__jit_icall_native_%s", ji->data.name); + break; + case MONO_PATCH_INFO_GENERIC_CLASS_INIT: + debug_sym = g_strdup_printf ("plt__generic_class_init"); + break; + default: + break; + } + + return debug_sym; +} + /* * Calls made from AOTed code are routed through a table of jumps similar to the - * ELF PLT (Program Linkage Table). The differences are the following: - * - the ELF PLT entries make an indirect jump though the GOT so they expect the - * GOT pointer to be in EBX. We want to avoid this, so our table contains direct - * jumps. This means the jumps need to be patched when the address of the callee is - * known. Initially the PLT entries jump to code which transfers control to the - * AOT runtime through the first PLT entry. + * ELF PLT (Program Linkage Table). Initially the PLT entries jump to code which transfers + * control to the AOT runtime through a trampoline. */ static void emit_plt (MonoAotCompile *acfg) @@ -3844,28 +4187,23 @@ emit_plt (MonoAotCompile *acfg) sprintf (symbol, "plt"); emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, TRUE); - emit_alignment (acfg, 16); + emit_alignment (acfg, NACL_SIZE(16, kNaClAlignment)); emit_label (acfg, symbol); emit_label (acfg, acfg->plt_symbol); for (i = 0; i < acfg->plt_offset; ++i) { - char label [128]; char *debug_sym = NULL; MonoPltEntry *plt_entry = NULL; MonoJumpInfo *ji; - if (i == 0) { + if (i == 0) /* - * The first plt entry is used to transfer code to the AOT loader. + * The first plt entry is unused. */ - arch_emit_plt_entry (acfg, i); continue; - } plt_entry = g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); ji = plt_entry->ji; - sprintf (label, "%s", plt_entry->symbol); if (acfg->llvm) { /* @@ -3878,11 +4216,10 @@ emit_plt (MonoAotCompile *acfg) if (ji && is_direct_callable (acfg, NULL, ji) && !acfg->use_bin_writer) { MonoCompile *callee_cfg = g_hash_table_lookup (acfg->method_to_cfg, ji->data.method); - if (acfg->thumb_mixed) { + if (acfg->thumb_mixed && !callee_cfg->compile_llvm) { /* LLVM calls the PLT entries using bl, so emit a stub */ - /* FIXME: Too much overhead on every call */ + fprintf (acfg->fp, "\n.thumb_func\n"); emit_label (acfg, plt_entry->llvm_symbol); - fprintf (acfg->fp, ".thumb_func\n"); fprintf (acfg->fp, "bx pc\n"); fprintf (acfg->fp, "nop\n"); fprintf (acfg->fp, ".arm\n"); @@ -3894,69 +4231,87 @@ emit_plt (MonoAotCompile *acfg) } } - emit_label (acfg, plt_entry->llvm_symbol); + if (acfg->aot_opts.write_symbols) + plt_entry->debug_sym = get_plt_entry_debug_sym (acfg, ji, cache); + debug_sym = plt_entry->debug_sym; + + if (acfg->thumb_mixed && !plt_entry->jit_used) + /* Emit only a thumb version */ + continue; + + if (!acfg->thumb_mixed) + emit_label (acfg, plt_entry->llvm_symbol); - if (acfg->thumb_mixed) { - /* LLVM calls the PLT entries using bl, so emit a stub */ - /* FIXME: Too much overhead on every call */ - fprintf (acfg->fp, ".thumb_func\n"); - fprintf (acfg->fp, "bx pc\n"); - fprintf (acfg->fp, "nop\n"); - fprintf (acfg->fp, ".arm\n"); + if (debug_sym) { + if (acfg->need_no_dead_strip) + fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym); + emit_local_symbol (acfg, debug_sym, NULL, TRUE); + emit_label (acfg, debug_sym); } - emit_label (acfg, label); + emit_label (acfg, plt_entry->symbol); - if (acfg->aot_opts.write_symbols) { - switch (ji->type) { - case MONO_PATCH_INFO_METHOD: - debug_sym = get_debug_sym (ji->data.method, "plt_", cache); - break; - case MONO_PATCH_INFO_INTERNAL_METHOD: - debug_sym = g_strdup_printf ("plt__jit_icall_%s", ji->data.name); - break; - case MONO_PATCH_INFO_CLASS_INIT: - debug_sym = g_strdup_printf ("plt__class_init_%s", mono_type_get_name (&ji->data.klass->byval_arg)); - sanitize_symbol (debug_sym); - break; - case MONO_PATCH_INFO_RGCTX_FETCH: - debug_sym = g_strdup_printf ("plt__rgctx_fetch_%d", acfg->label_generator ++); - break; - case MONO_PATCH_INFO_ICALL_ADDR: { - char *s = get_debug_sym (ji->data.method, "", cache); - - debug_sym = g_strdup_printf ("plt__icall_native_%s", s); - g_free (s); - break; - } - case MONO_PATCH_INFO_JIT_ICALL_ADDR: - debug_sym = g_strdup_printf ("plt__jit_icall_native_%s", ji->data.name); - break; - case MONO_PATCH_INFO_GENERIC_CLASS_INIT: - debug_sym = g_strdup_printf ("plt__generic_class_init"); - break; - default: - break; + arch_emit_plt_entry (acfg, i); + + if (debug_sym) + emit_symbol_size (acfg, debug_sym, "."); + } + + if (acfg->thumb_mixed) { + /* Make sure the ARM symbols don't alias the thumb ones */ + emit_zero_bytes (acfg, 16); + + /* + * Emit a separate set of PLT entries using thumb2 which is called by LLVM generated + * code. + */ + for (i = 0; i < acfg->plt_offset; ++i) { + char *debug_sym = NULL; + MonoPltEntry *plt_entry = NULL; + MonoJumpInfo *ji; + + if (i == 0) + continue; + + plt_entry = g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); + ji = plt_entry->ji; + + if (ji && is_direct_callable (acfg, NULL, ji) && !acfg->use_bin_writer) + continue; + + /* Skip plt entries not actually called by LLVM code */ + if (!plt_entry->llvm_used) + continue; + + if (acfg->aot_opts.write_symbols) { + if (plt_entry->debug_sym) + debug_sym = g_strdup_printf ("%s_thumb", plt_entry->debug_sym); } if (debug_sym) { +#if defined(__APPLE__) + fprintf (acfg->fp, " .thumb_func %s\n", debug_sym); + fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym); +#endif emit_local_symbol (acfg, debug_sym, NULL, TRUE); emit_label (acfg, debug_sym); } - } + fprintf (acfg->fp, "\n.thumb_func\n"); - arch_emit_plt_entry (acfg, i); + emit_label (acfg, plt_entry->llvm_symbol); - if (debug_sym) { - emit_symbol_size (acfg, debug_sym, "."); - g_free (debug_sym); + arch_emit_llvm_plt_entry (acfg, i); + + if (debug_sym) { + emit_symbol_size (acfg, debug_sym, "."); + g_free (debug_sym); + } } } emit_symbol_size (acfg, acfg->plt_symbol, "."); sprintf (symbol, "plt_end"); - emit_global (acfg, symbol, TRUE); emit_label (acfg, symbol); g_hash_table_destroy (cache); @@ -4049,11 +4404,11 @@ static void emit_trampolines (MonoAotCompile *acfg) { char symbol [256]; + char end_symbol [256]; int i, tramp_got_offset; MonoAotTrampoline ntype; #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES int tramp_type; - guint8 *code; #endif if (!acfg->aot_opts.full_aot) @@ -4091,15 +4446,15 @@ emit_trampolines (MonoAotCompile *acfg) emit_trampoline (acfg, acfg->got_offset, info); /* Emit the exception related code pieces */ - code = mono_arch_get_restore_context (&info, TRUE); + mono_arch_get_restore_context (&info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); - code = mono_arch_get_call_filter (&info, TRUE); + mono_arch_get_call_filter (&info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); - code = mono_arch_get_throw_exception (&info, TRUE); + mono_arch_get_throw_exception (&info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); - code = mono_arch_get_rethrow_exception (&info, TRUE); + mono_arch_get_rethrow_exception (&info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); - code = mono_arch_get_throw_corlib_exception (&info, TRUE); + mono_arch_get_throw_corlib_exception (&info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); #if defined(MONO_ARCH_HAVE_GET_TRAMPOLINES) @@ -4119,11 +4474,11 @@ emit_trampolines (MonoAotCompile *acfg) int offset; offset = MONO_RGCTX_SLOT_MAKE_RGCTX (i); - code = mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE); + mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); offset = MONO_RGCTX_SLOT_MAKE_MRGCTX (i); - code = mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE); + mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE); emit_trampoline (acfg, acfg->got_offset, info); } @@ -4191,7 +4546,11 @@ emit_trampolines (MonoAotCompile *acfg) g_assert_not_reached (); } - emit_global (acfg, symbol, TRUE); + sprintf (end_symbol, "%s_e", symbol); + + if (acfg->aot_opts.write_symbols) + emit_local_symbol (acfg, symbol, end_symbol, TRUE); + emit_alignment (acfg, AOT_FUNC_ALIGNMENT); emit_label (acfg, symbol); @@ -4226,6 +4585,8 @@ emit_trampolines (MonoAotCompile *acfg) acfg->trampoline_size [ntype] = tramp_size; } } + + emit_label (acfg, end_symbol); } /* Reserve some entries at the end of the GOT for our use */ @@ -4259,6 +4620,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) opts->save_temps = TRUE; } else if (str_begins_with (arg, "write-symbols")) { opts->write_symbols = TRUE; + } else if (str_begins_with (arg, "no-write-symbols")) { + opts->write_symbols = FALSE; } else if (str_begins_with (arg, "metadata-only")) { opts->metadata_only = TRUE; } else if (str_begins_with (arg, "bind-to-runtime-version")) { @@ -4294,6 +4657,36 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) opts->stats = TRUE; } else if (str_begins_with (arg, "mtriple=")) { opts->mtriple = g_strdup (arg + strlen ("mtriple=")); + } else if (str_begins_with (arg, "llvm-path=")) { + opts->llvm_path = g_strdup (arg + strlen ("llvm-path=")); + } else if (str_begins_with (arg, "info")) { + printf ("AOT target setup: %s.\n", AOT_TARGET_STR); + exit (0); + } else if (str_begins_with (arg, "help") || str_begins_with (arg, "?")) { + printf ("Supported options for --aot:\n"); + printf (" outfile=\n"); + printf (" save-temps\n"); + printf (" keep-temps\n"); + printf (" write-symbols\n"); + printf (" metadata-only\n"); + printf (" bind-to-runtime-version\n"); + printf (" full\n"); + printf (" threads=\n"); + printf (" static\n"); + printf (" asmonly\n"); + printf (" asmwriter\n"); + printf (" nodebug\n"); + printf (" ntrampolines=\n"); + printf (" nrgctx-trampolines=\n"); + printf (" nimt-trampolines=\n"); + printf (" autoreg\n"); + printf (" tool-prefix=\n"); + printf (" soft-debug\n"); + printf (" print-skipped\n"); + printf (" stats\n"); + printf (" info\n"); + printf (" help/?\n"); + exit (0); } else { fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg); exit (1); @@ -4359,6 +4752,13 @@ can_encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info) return TRUE; else return FALSE; + case MONO_WRAPPER_CASTCLASS: + if (!strcmp (method->name, "__castclass_with_cache")) + return TRUE; + else if (!strcmp (method->name, "__isinst_with_cache")) + return TRUE; + else + return FALSE; default: //printf ("Skip (wrapper call): %d -> %s\n", patch_info->type, mono_method_full_name (patch_info->data.method, TRUE)); return FALSE; @@ -4462,6 +4862,7 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) return; } if (cfg->exception_type != MONO_EXCEPTION_NONE) { + //printf ("E: %s\n", mono_method_full_name (method, TRUE)); /* Let the exception happen at runtime */ return; } @@ -4714,7 +5115,8 @@ load_profile_files (MonoAotCompile *acfg) int file_index, res, method_index, i; char ver [256]; guint32 token; - GList *unordered; + GList *unordered, *l; + gboolean found; file_index = 0; while (TRUE) { @@ -4760,9 +5162,15 @@ load_profile_files (MonoAotCompile *acfg) token = mono_method_get_token (method); method_index = mono_metadata_token_index (token) - 1; - if (!g_list_find (acfg->method_order, GUINT_TO_POINTER (method_index))) { - acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (method_index)); + found = FALSE; + for (i = 0; i < acfg->method_order->len; ++i) { + if (g_ptr_array_index (acfg->method_order, i) == GUINT_TO_POINTER (method_index)) { + found = TRUE; + break; + } } + if (!found) + g_ptr_array_add (acfg->method_order, GUINT_TO_POINTER (method_index)); } else { //printf ("No method found matching '%s'.\n", name); } @@ -4772,15 +5180,20 @@ load_profile_files (MonoAotCompile *acfg) /* Add missing methods */ unordered = NULL; - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - if (!g_list_find (acfg->method_order, GUINT_TO_POINTER (i))) - unordered = g_list_prepend (unordered, GUINT_TO_POINTER (i)); + for (method_index = 0; method_index < acfg->image->tables [MONO_TABLE_METHOD].rows; ++method_index) { + found = FALSE; + for (i = 0; i < acfg->method_order->len; ++i) { + if (g_ptr_array_index (acfg->method_order, i) == GUINT_TO_POINTER (method_index)) { + found = TRUE; + break; + } + } + if (!found) + unordered = g_list_prepend (unordered, GUINT_TO_POINTER (method_index)); } unordered = g_list_reverse (unordered); - if (acfg->method_order) - g_list_last (acfg->method_order)->next = unordered; - else - acfg->method_order = unordered; + for (l = unordered; l; l = l->next) + g_ptr_array_add (acfg->method_order, l->data); } /* Used by the LLVM backend */ @@ -4793,7 +5206,11 @@ mono_aot_get_got_offset (MonoJumpInfo *ji) char* mono_aot_get_method_name (MonoCompile *cfg) { - return get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash); + if (llvm_acfg->aot_opts.static_link) + /* Include the assembly name too to avoid duplicate symbol errors */ + return g_strdup_printf ("%s_%s", llvm_acfg->image->assembly->aname.name, get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash)); + else + return get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash); } char* @@ -4809,8 +5226,13 @@ mono_aot_get_plt_symbol (MonoJumpInfoType type, gconstpointer data) return NULL; plt_entry = get_plt_entry (llvm_acfg, ji); + plt_entry->llvm_used = TRUE; +#if defined(__APPLE__) + return g_strdup_printf (plt_entry->llvm_symbol + strlen (llvm_acfg->llvm_label_prefix)); +#else return g_strdup_printf (plt_entry->llvm_symbol); +#endif } MonoJumpInfo* @@ -4891,9 +5313,9 @@ emit_llvm_file (MonoAotCompile *acfg) * then removing tailcallelim + the global opts, and adding a second gvn. */ opts = g_strdup ("-instcombine -simplifycfg"); - opts = g_strdup ("-simplifycfg -domtree -domfrontier -scalarrepl -instcombine -simplifycfg -basiccg -prune-eh -inline -functionattrs -domtree -domfrontier -scalarrepl -simplify-libcalls -instcombine -simplifycfg -instcombine -simplifycfg -reassociate -domtree -loops -loopsimplify -domfrontier -loopsimplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -instcombine -scalar-evolution -loopsimplify -lcssa -iv-users -indvars -loop-deletion -loopsimplify -lcssa -loop-unroll -instcombine -memdep -gvn -memdep -memcpyopt -sccp -instcombine -domtree -memdep -dse -adce -gvn -simplifycfg -preverify -domtree -verify"); + opts = g_strdup ("-simplifycfg -domtree -domfrontier -scalarrepl -instcombine -simplifycfg -basiccg -prune-eh -inline -functionattrs -domtree -domfrontier -scalarrepl -simplify-libcalls -instcombine -simplifycfg -instcombine -simplifycfg -reassociate -domtree -loops -loop-simplify -domfrontier -loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -instcombine -scalar-evolution -loop-simplify -lcssa -iv-users -indvars -loop-deletion -loop-simplify -lcssa -loop-unroll -instcombine -memdep -gvn -memdep -memcpyopt -sccp -instcombine -domtree -memdep -dse -adce -simplifycfg -preverify -domtree -verify"); #if 1 - command = g_strdup_printf ("opt -f %s -o temp.opt.bc temp.bc", opts); + command = g_strdup_printf ("%sopt -f %s -o temp.opt.bc temp.bc", acfg->aot_opts.llvm_path, opts); printf ("Executing opt: %s\n", command); if (system (command) != 0) { exit (1); @@ -4910,9 +5332,13 @@ emit_llvm_file (MonoAotCompile *acfg) if (acfg->aot_opts.mtriple) g_string_append_printf (acfg->llc_args, " -mtriple=%s", acfg->aot_opts.mtriple); + if (llvm_acfg->aot_opts.static_link) + g_string_append_printf (acfg->llc_args, " -relocation-model=static"); + else + g_string_append_printf (acfg->llc_args, " -relocation-model=pic"); unlink (acfg->tmpfname); - command = g_strdup_printf ("llc %s -relocation-model=pic -unwind-tables -disable-gnu-eh-frame -enable-mono-eh-frame -o %s temp.opt.bc", acfg->llc_args->str, acfg->tmpfname); + command = g_strdup_printf ("%sllc %s -unwind-tables -disable-gnu-eh-frame -enable-mono-eh-frame -o %s temp.opt.bc", acfg->aot_opts.llvm_path, acfg->llc_args->str, acfg->tmpfname); printf ("Executing llc: %s\n", command); @@ -4925,10 +5351,9 @@ emit_llvm_file (MonoAotCompile *acfg) static void emit_code (MonoAotCompile *acfg) { - int i; + int oindex, i; char symbol [256]; char end_symbol [256]; - GList *l; #if defined(TARGET_POWERPC64) sprintf (symbol, ".Lgot_addr"); @@ -4964,13 +5389,22 @@ emit_code (MonoAotCompile *acfg) * Emit some padding so the local symbol for the first method doesn't have the * same address as 'methods'. */ +#if defined(__default_codegen__) emit_zero_bytes (acfg, 16); +#elif defined(__native_client_codegen__) + { + const int kPaddingSize = 16; + guint8 pad_buffer[kPaddingSize]; + mono_arch_nacl_pad (pad_buffer, kPaddingSize); + emit_bytes (acfg, pad_buffer, kPaddingSize); + } +#endif - for (l = acfg->method_order; l != NULL; l = l->next) { + for (oindex = 0; oindex < acfg->method_order->len; ++oindex) { MonoCompile *cfg; MonoMethod *method; - i = GPOINTER_TO_UINT (l->data); + i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex)); cfg = acfg->cfgs [i]; @@ -4981,8 +5415,6 @@ emit_code (MonoAotCompile *acfg) /* Emit unbox trampoline */ if (acfg->aot_opts.full_aot && cfg->orig_method->klass->valuetype && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { - char call_target [256]; - if (!method->wrapper_type && !method->is_inflated) { g_assert (method->token); sprintf (symbol, "ut_%d", mono_metadata_token_index (method->token) - 1); @@ -4995,11 +5427,13 @@ emit_code (MonoAotCompile *acfg) emit_alignment (acfg, AOT_FUNC_ALIGNMENT); #endif emit_global (acfg, symbol, TRUE); - emit_label (acfg, symbol); - sprintf (call_target, "%s", cfg->asm_symbol); + if (acfg->thumb_mixed && cfg->compile_llvm) + fprintf (acfg->fp, "\n.thumb_func\n"); - arch_emit_unbox_trampoline (acfg, cfg->orig_method, call_target); + emit_label (acfg, symbol); + + arch_emit_unbox_trampoline (acfg, cfg, cfg->orig_method, cfg->asm_symbol); } if (cfg->compile_llvm) @@ -5010,13 +5444,26 @@ emit_code (MonoAotCompile *acfg) sprintf (symbol, "methods_end"); emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); + /* + * Add .no_dead_strip directives for all LLVM methods to prevent the OSX linker + * from optimizing them away, since it doesn't see that code_offsets references them. + * JITted methods don't need this since they are referenced using assembler local + * symbols. + * FIXME: This is why write-symbols doesn't work on OSX ? + */ + if (acfg->llvm && acfg->need_no_dead_strip) { + fprintf (acfg->fp, "\n"); + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i] && acfg->cfgs [i]->compile_llvm) + fprintf (acfg->fp, ".no_dead_strip %s\n", acfg->cfgs [i]->asm_symbol); + } + } + sprintf (symbol, "code_offsets"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5036,15 +5483,14 @@ emit_code (MonoAotCompile *acfg) static void emit_info (MonoAotCompile *acfg) { - int i; + int oindex, i; char symbol [256]; - GList *l; gint32 *offsets; offsets = g_new0 (gint32, acfg->nmethods); - for (l = acfg->method_order; l != NULL; l = l->next) { - i = GPOINTER_TO_UINT (l->data); + for (oindex = 0; oindex < acfg->method_order->len; ++oindex) { + i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex)); if (acfg->cfgs [i]) { emit_method_info (acfg, acfg->cfgs [i]); @@ -5056,7 +5502,6 @@ emit_info (MonoAotCompile *acfg) sprintf (symbol, "method_info_offsets"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5104,8 +5549,9 @@ mono_aot_type_hash (MonoType *t1) return ((hash << 5) - hash) ^ mono_metadata_type_hash (&t1->data.array->eklass->byval_arg); case MONO_TYPE_GENERICINST: return ((hash << 5) - hash) ^ 0; + default: + return hash; } - return hash; } /* @@ -5420,7 +5866,6 @@ emit_extra_methods (MonoAotCompile *acfg) /* Emit the table */ sprintf (symbol, "extra_method_table"); emit_section_change (acfg, RODATA_SECT, 0); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5449,7 +5894,6 @@ emit_extra_methods (MonoAotCompile *acfg) */ sprintf (symbol, "extra_method_info_offsets"); emit_section_change (acfg, RODATA_SECT, 0); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5481,7 +5925,6 @@ emit_exception_info (MonoAotCompile *acfg) sprintf (symbol, "ex_info_offsets"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5505,7 +5948,6 @@ emit_unwind_info (MonoAotCompile *acfg) emit_section_change (acfg, RODATA_SECT, 1); emit_alignment (acfg, 8); emit_label (acfg, symbol); - emit_global (acfg, symbol, FALSE); for (i = 0; i < acfg->unwind_ops->len; ++i) { guint32 index = GPOINTER_TO_UINT (g_ptr_array_index (acfg->unwind_ops, i)); @@ -5538,7 +5980,6 @@ emit_class_info (MonoAotCompile *acfg) sprintf (symbol, "class_info_offsets"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5572,8 +6013,10 @@ emit_class_name_table (MonoAotCompile *acfg) for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { token = MONO_TOKEN_TYPE_DEF | (i + 1); klass = mono_class_get (acfg->image, token); - if (!klass) + if (!klass) { + mono_loader_clear_error (); continue; + } full_name = mono_type_get_name_full (mono_class_get_type (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME); hash = mono_metadata_str_hash (full_name) % table_size; g_free (full_name); @@ -5599,7 +6042,6 @@ emit_class_name_table (MonoAotCompile *acfg) /* Emit the table */ sprintf (symbol, "class_name_table"); emit_section_change (acfg, RODATA_SECT, 0); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5634,9 +6076,8 @@ emit_image_table (MonoAotCompile *acfg) * So we emit it at once, and reference its elements by an index. */ - sprintf (symbol, "mono_image_table"); + sprintf (symbol, "image_table"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5719,7 +6160,6 @@ emit_got_info (MonoAotCompile *acfg) /* Emit got_info_offsets table */ sprintf (symbol, "got_info_offsets"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -5753,7 +6193,7 @@ typedef struct GlobalsTableEntry { } GlobalsTableEntry; static void -emit_globals_table (MonoAotCompile *acfg) +emit_globals (MonoAotCompile *acfg) { int i, table_size; guint32 hash; @@ -5761,6 +6201,13 @@ emit_globals_table (MonoAotCompile *acfg) char symbol [256]; GlobalsTableEntry *entry, *new_entry; + if (!acfg->aot_opts.static_link) + return; + + /* + * When static linking, we emit a table containing our globals. + */ + /* * Construct a chained hash table for mapping global names to their index in * the globals table. @@ -5822,12 +6269,15 @@ emit_globals_table (MonoAotCompile *acfg) sprintf (symbol, "name_%d", i); emit_section_change (acfg, RODATA_SECT, 1); +#ifdef __APPLE__ + emit_alignment (acfg, 4); +#endif emit_label (acfg, symbol); emit_string (acfg, name); } /* Emit the globals table */ - sprintf (symbol, ".Lglobals"); + sprintf (symbol, "globals"); emit_section_change (acfg, ".data", 0); /* This is not a global, since it is accessed by the init function */ emit_alignment (acfg, 8); @@ -5850,60 +6300,6 @@ emit_globals_table (MonoAotCompile *acfg) emit_int32 (acfg, 0); } -static void -emit_globals (MonoAotCompile *acfg) -{ - char *build_info; - - emit_string_symbol (acfg, "mono_assembly_guid" , acfg->image->guid); - - emit_string_symbol (acfg, "mono_aot_version", MONO_AOT_FILE_VERSION); - - if (acfg->aot_opts.bind_to_runtime_version) { - build_info = mono_get_runtime_build_info (); - emit_string_symbol (acfg, "mono_runtime_version", build_info); - g_free (build_info); - } else { - emit_string_symbol (acfg, "mono_runtime_version", ""); - } - - /* - * When static linking, we emit a global which will point to the symbol table. - */ - if (acfg->aot_opts.static_link) { - char symbol [256]; - char *p; - - /* Emit a string holding the assembly name */ - emit_string_symbol (acfg, "mono_aot_assembly_name", acfg->image->assembly->aname.name); - - emit_globals_table (acfg); - - /* - * Emit a global symbol which can be passed by an embedding app to - * mono_aot_register_module (). - */ -#if defined(__MACH__) && !defined(__native_client_codegen__) - sprintf (symbol, "_mono_aot_module_%s_info", acfg->image->assembly->aname.name); -#else - sprintf (symbol, "mono_aot_module_%s_info", acfg->image->assembly->aname.name); -#endif - - /* Get rid of characters which cannot occur in symbols */ - p = symbol; - for (p = symbol; *p; ++p) { - if (!(isalnum (*p) || *p == '_')) - *p = '_'; - } - acfg->static_linking_symbol = g_strdup (symbol); - emit_global_inner (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - sprintf (symbol, "%sglobals", acfg->temp_prefix); - emit_pointer (acfg, symbol); - } -} - static void emit_autoreg (MonoAotCompile *acfg) { @@ -5930,7 +6326,6 @@ emit_mem_end (MonoAotCompile *acfg) sprintf (symbol, "mem_end"); emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); } @@ -5941,10 +6336,24 @@ emit_mem_end (MonoAotCompile *acfg) static void emit_file_info (MonoAotCompile *acfg) { - char symbol [128]; + char symbol [256]; int i; int gc_name_offset; const char *gc_name; + char *build_info; + + emit_string_symbol (acfg, "assembly_guid" , acfg->image->guid); + + if (acfg->aot_opts.bind_to_runtime_version) { + build_info = mono_get_runtime_build_info (); + emit_string_symbol (acfg, "runtime_version", build_info); + g_free (build_info); + } else { + emit_string_symbol (acfg, "runtime_version", ""); + } + + /* Emit a string holding the assembly name */ + emit_string_symbol (acfg, "assembly_name", acfg->image->assembly->aname.name); /* * The managed allocators are GC specific, so can't use an AOT image created by one GC @@ -5957,13 +6366,18 @@ emit_file_info (MonoAotCompile *acfg) emit_section_change (acfg, ".data", 0); emit_alignment (acfg, 8); emit_label (acfg, symbol); - emit_global (acfg, symbol, FALSE); + if (!acfg->aot_opts.static_link) + emit_global (acfg, symbol, FALSE); /* The data emitted here must match MonoAotFileInfo. */ + emit_int32 (acfg, MONO_AOT_FILE_VERSION); + emit_int32 (acfg, 0); + /* - * We emit these as data to avoid making these symbols global, which leads to - * various problems + * We emit pointers to our data structures instead of emitting global symbols which + * point to them, to reduce the number of globals, and because using globals leads to + * various problems (i.e. arm/thumb). */ emit_pointer (acfg, acfg->got_symbol); emit_pointer (acfg, "methods"); @@ -5975,6 +6389,43 @@ emit_file_info (MonoAotCompile *acfg) } else { emit_pointer (acfg, NULL); } + emit_pointer (acfg, "blob"); + emit_pointer (acfg, "class_name_table"); + emit_pointer (acfg, "class_info_offsets"); + emit_pointer (acfg, "method_info_offsets"); + emit_pointer (acfg, "ex_info_offsets"); + emit_pointer (acfg, "code_offsets"); + emit_pointer (acfg, "extra_method_info_offsets"); + emit_pointer (acfg, "extra_method_table"); + emit_pointer (acfg, "got_info_offsets"); + emit_pointer (acfg, "methods_end"); + emit_pointer (acfg, "unwind_info"); + emit_pointer (acfg, "mem_end"); + emit_pointer (acfg, "image_table"); + emit_pointer (acfg, "plt"); + emit_pointer (acfg, "plt_end"); + emit_pointer (acfg, "assembly_guid"); + emit_pointer (acfg, "runtime_version"); + if (acfg->num_trampoline_got_entries) { + emit_pointer (acfg, "specific_trampolines"); + emit_pointer (acfg, "static_rgctx_trampolines"); + emit_pointer (acfg, "imt_thunks"); + } else { + emit_pointer (acfg, NULL); + emit_pointer (acfg, NULL); + emit_pointer (acfg, NULL); + } + if (acfg->thumb_mixed) { + emit_pointer (acfg, "thumb_end"); + } else { + emit_pointer (acfg, NULL); + } + if (acfg->aot_opts.static_link) { + emit_pointer (acfg, "globals"); + } else { + emit_pointer (acfg, NULL); + } + emit_pointer (acfg, "assembly_name"); emit_int32 (acfg, acfg->plt_got_offset_base); emit_int32 (acfg, (int)(acfg->got_offset * sizeof (gpointer))); @@ -5990,6 +6441,52 @@ emit_file_info (MonoAotCompile *acfg) emit_int32 (acfg, acfg->trampoline_got_offset_base [i]); for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) emit_int32 (acfg, acfg->trampoline_size [i]); + +#if defined (TARGET_ARM) && defined (__APPLE__) + { + MonoType t; + int align = 0; + + t.type = MONO_TYPE_R8; + mono_type_size (&t, &align); + + emit_int32 (acfg, align); + + t.type = MONO_TYPE_I8; + mono_type_size (&t, &align); + + emit_int32 (acfg, align); + } +#else + emit_int32 (acfg, __alignof__ (double)); + emit_int32 (acfg, __alignof__ (gint64)); +#endif + + if (acfg->aot_opts.static_link) { + char *p; + + /* + * Emit a global symbol which can be passed by an embedding app to + * mono_aot_register_module (). The symbol points to a pointer to the the file info + * structure. + */ +#if defined(__APPLE__) && !defined(__native_client_codegen__) + sprintf (symbol, "_mono_aot_module_%s_info", acfg->image->assembly->aname.name); +#else + sprintf (symbol, "mono_aot_module_%s_info", acfg->image->assembly->aname.name); +#endif + + /* Get rid of characters which cannot occur in symbols */ + p = symbol; + for (p = symbol; *p; ++p) { + if (!(isalnum (*p) || *p == '_')) + *p = '_'; + } + acfg->static_linking_symbol = g_strdup (symbol); + emit_global_inner (acfg, symbol, FALSE); + emit_label (acfg, symbol); + emit_pointer (acfg, "mono_aot_file_info"); + } } static void @@ -5999,7 +6496,6 @@ emit_blob (MonoAotCompile *acfg) sprintf (symbol, "blob"); emit_section_change (acfg, RODATA_SECT, 1); - emit_global (acfg, symbol, FALSE); emit_alignment (acfg, 8); emit_label (acfg, symbol); @@ -6168,7 +6664,11 @@ compile_asm (MonoAotCompile *acfg) #endif #ifdef __native_client_codegen__ +#if defined(TARGET_AMD64) +#define AS_NAME "nacl64-as" +#else #define AS_NAME "nacl-as" +#endif #else #define AS_NAME "as" #endif @@ -6220,7 +6720,7 @@ compile_asm (MonoAotCompile *acfg) #if defined(sparc) command = g_strdup_printf ("ld -shared -G -o %s %s.o", tmp_outfile_name, acfg->tmpfname); -#elif defined(__ppc__) && defined(__MACH__) +#elif defined(__ppc__) && defined(__APPLE__) command = g_strdup_printf ("gcc -dynamiclib -o %s %s.o", tmp_outfile_name, acfg->tmpfname); #elif defined(HOST_WIN32) command = g_strdup_printf ("gcc -shared --dll -mno-cygwin -o %s %s.o", tmp_outfile_name, acfg->tmpfname); @@ -6245,7 +6745,7 @@ compile_asm (MonoAotCompile *acfg) system (com); g_free (com);*/ -#if defined(TARGET_ARM) && !defined(__MACH__) +#if defined(TARGET_ARM) && !defined(__APPLE__) /* * gas generates 'mapping symbols' each time code and data is mixed, which * happens a lot in emit_and_reloc_code (), so we need to get rid of them. @@ -6305,6 +6805,7 @@ acfg_create (MonoAssembly *ass, guint32 opts) acfg->unwind_info_offsets = g_hash_table_new (NULL, NULL); acfg->unwind_ops = g_ptr_array_new (); acfg->method_label_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + acfg->method_order = g_ptr_array_new (); InitializeCriticalSection (&acfg->mutex); return acfg; @@ -6364,6 +6865,7 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) acfg->aot_opts.ntrampolines = 1024; acfg->aot_opts.nrgctx_trampolines = 1024; acfg->aot_opts.nimt_trampolines = 128; + acfg->aot_opts.llvm_path = g_strdup (""); mono_aot_parse_options (aot_options, &acfg->aot_opts); @@ -6399,6 +6901,11 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) acfg->llvm = TRUE; acfg->aot_opts.asm_writer = TRUE; acfg->flags |= MONO_AOT_FILE_FLAG_WITH_LLVM; + + if (acfg->aot_opts.soft_debug) { + fprintf (stderr, "The 'soft-debug' option is not supported when compiling with LLVM.\n"); + exit (1); + } } if (acfg->aot_opts.full_aot) @@ -6416,7 +6923,7 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) arch_init (acfg); - acfg->got_symbol_base = g_strdup_printf ("%smono_aot_%s_got", acfg->llvm_label_prefix, acfg->image->assembly->aname.name); + acfg->got_symbol_base = g_strdup_printf ("mono_aot_%s_got", acfg->image->assembly->aname.name); acfg->plt_symbol = g_strdup_printf ("%smono_aot_%s_plt", acfg->llvm_label_prefix, acfg->image->assembly->aname.name); /* Get rid of characters which cannot occur in symbols */ @@ -6567,9 +7074,8 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) */ sprintf (symbol, "thumb_end"); emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); emit_label (acfg, symbol); - fprintf (acfg->fp, ".skip 16\n"); + emit_zero_bytes (acfg, 16); fprintf (acfg->fp, ".arm\n"); } @@ -6613,6 +7119,13 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) emit_mem_end (acfg); + if (acfg->need_pt_gnu_stack) { + /* This is required so the .so doesn't have an executable stack */ + /* The bin writer already emits this */ + if (!acfg->use_bin_writer) + fprintf (acfg->fp, "\n.section .note.GNU-stack,\"\",@progbits\n"); + } + TV_GETTIME (btv); acfg->stats.gen_time = TV_ELAPSED (atv, btv);