Add support to the x86 backend for generating code compatible with Google
[mono.git] / mono / mini / aot-compiler.c
index 01e0eb95a3c6f5581ab46203a0ce817d29b27c33..86ebb1dc28df93339857ebb631dc80d704447865 100644 (file)
@@ -68,7 +68,7 @@
 
 #if !defined(DISABLE_AOT) && !defined(DISABLE_JIT)
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__native_client_codegen__)
 #define RODATA_SECT ".rodata"
 #else
 #define RODATA_SECT ".text"
@@ -330,6 +330,14 @@ emit_byte (MonoAotCompile *acfg, guint8 val)
        img_writer_emit_byte (acfg->w, val); 
 }
 
+#ifdef __native_client_codegen__
+static inline void
+emit_nacl_call_alignment (MonoAotCompile *acfg)
+{
+       img_writer_emit_nacl_call_alignment (acfg->w);
+}
+#endif
+
 static G_GNUC_UNUSED void
 emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func)
 {
@@ -460,6 +468,10 @@ encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf)
 #else
 #define AOT_FUNC_ALIGNMENT 16
 #endif
+#if defined(TARGET_X86) && defined(__native_client_codegen__)
+#undef AOT_FUNC_ALIGNMENT
+#define AOT_FUNC_ALIGNMENT 32
+#endif
  
 #if defined(TARGET_POWERPC64) && !defined(__mono_ilp32__)
 #define PPC_LD_OP "ld"
@@ -654,12 +666,26 @@ 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__
+               const guint8 kSizeOfNaClJmp = 11;
+               guint8 bytes[kSizeOfNaClJmp];
+               guint8 *pbytes = &bytes[0];
+               
+               x86_jump_membase32 (pbytes, X86_EBX, offset);
+               emit_bytes (acfg, bytes, kSizeOfNaClJmp);
+               /* four bytes of data, used by mono_arch_patch_plt_entry              */
+               /* For Native Client, make this work with data embedded in push.      */
+               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 *<offset>(%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__ */
 #elif defined(TARGET_AMD64)
                /*
                 * We can't emit jumps because they are 32 bits only so they can't be patched.
@@ -846,9 +872,16 @@ arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size
        /* Branch to generic trampoline */
        x86_jump_reg (code, X86_ECX);
 
+#ifdef __native_client_codegen__
+       {
+               /* emit nops to next 32 byte alignment */
+               int a = (~kNaClAlignmentMask) & ((code - buf) + kNaClAlignment - 1);
+               while (code < (buf + a)) x86_nop(code);
+       }
+#endif
        emit_bytes (acfg, buf, code - buf);
 
-       *tramp_size = 17;
+       *tramp_size = NACL_SIZE(17, kNaClAlignment);
        g_assert (code - buf == *tramp_size);
 #else
        g_assert_not_reached ();
@@ -882,9 +915,6 @@ arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, MonoGeneri
        guint8 *code;
        int this_pos = 4;
 
-       if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret))
-               this_pos = 8;
-           
        code = buf;
 
        x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject));
@@ -897,14 +927,10 @@ arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, MonoGeneri
 #elif defined(TARGET_ARM)
        guint8 buf [128];
        guint8 *code;
-       int this_pos = 0;
 
        code = buf;
 
-       if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret))
-               this_pos = 1;
-
-       ARM_ADD_REG_IMM8 (code, this_pos, this_pos, sizeof (MonoObject));
+       ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
 
        emit_bytes (acfg, buf, code - buf);
        /* jump to method */
@@ -923,9 +949,6 @@ arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, MonoGeneri
 #elif defined(TARGET_POWERPC)
        int this_pos = 3;
 
-       if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret))
-               this_pos = 4;
-
        g_assert (!acfg->use_bin_writer);
 
        fprintf (acfg->fp, "\n\taddi %d, %d, %d\n", this_pos, this_pos, (int)sizeof (MonoObject));
@@ -1038,9 +1061,17 @@ arch_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_
        /* Branch to the target address */
        x86_jump_membase (code, X86_ECX, (offset + 1) * sizeof (gpointer));
 
+#ifdef __native_client_codegen__
+       {
+               /* emit nops to next 32 byte alignment */
+               int a = (~kNaClAlignmentMask) & ((code - buf) + kNaClAlignment - 1);
+               while (code < (buf + a)) x86_nop(code);
+       }
+#endif
+
        emit_bytes (acfg, buf, code - buf);
 
-       *tramp_size = 15;
+       *tramp_size = NACL_SIZE (15, kNaClAlignment);
        g_assert (code - buf == *tramp_size);
 #else
        g_assert_not_reached ();
@@ -1070,36 +1101,38 @@ arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size)
        /* FIXME: Optimize this, i.e. use binary search etc. */
        /* Maybe move the body into a separate function (slower, but much smaller) */
 
-       /* R10 is a free register */
+       /* R11 is a free register */
 
        labels [0] = code;
-       amd64_alu_membase_imm (code, X86_CMP, AMD64_R10, 0, 0);
+       amd64_alu_membase_imm (code, X86_CMP, AMD64_R11, 0, 0);
        labels [1] = code;
        amd64_branch8 (code, X86_CC_Z, FALSE, 0);
 
        /* Check key */
-       amd64_alu_membase_reg (code, X86_CMP, AMD64_R10, 0, MONO_ARCH_IMT_REG);
+       amd64_alu_membase_reg (code, X86_CMP, AMD64_R11, 0, MONO_ARCH_IMT_REG);
        labels [2] = code;
        amd64_branch8 (code, X86_CC_Z, FALSE, 0);
 
        /* Loop footer */
-       amd64_alu_reg_imm (code, X86_ADD, AMD64_R10, 2 * sizeof (gpointer));
+       amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, 2 * sizeof (gpointer));
        amd64_jump_code (code, labels [0]);
 
        /* Match */
        mono_amd64_patch (labels [2], code);
-       amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, sizeof (gpointer), 8);
-       amd64_jump_membase (code, AMD64_R10, 0);
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, sizeof (gpointer), 8);
+       amd64_jump_membase (code, AMD64_R11, 0);
 
        /* No match */
        /* FIXME: */
        mono_amd64_patch (labels [1], code);
        x86_breakpoint (code);
 
-       /* mov <OFFSET>(%rip), %r10 */
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 12345678, 8);
+
+       /* mov <OFFSET>(%rip), %r11 */
        emit_byte (acfg, '\x4d');
        emit_byte (acfg, '\x8b');
-       emit_byte (acfg, '\x15');
+       emit_byte (acfg, '\x1d');
        emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (gpointer)) - 4);
 
        emit_bytes (acfg, buf, code - buf);
@@ -1107,9 +1140,17 @@ arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size)
        *tramp_size = code - buf + 7;
 #elif defined(TARGET_X86)
        guint8 *buf, *code;
+#ifdef __native_client_codegen__
+       guint8 *buf_alloc;
+#endif
        guint8 *labels [3];
 
+#ifdef __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 */
        x86_push_reg (code, X86_EAX);
@@ -1151,6 +1192,13 @@ arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size)
        mono_x86_patch (labels [1], code);
        x86_breakpoint (code);
 
+#ifdef __native_client_codegen__
+       {
+               /* emit nops to next 32 byte alignment */
+               int a = (~kNaClAlignmentMask) & ((code - buf) + kNaClAlignment - 1);
+               while (code < (buf + a)) x86_nop(code);
+       }
+#endif
        emit_bytes (acfg, buf, code - buf);
        
        *tramp_size = code - buf;
@@ -1425,7 +1473,7 @@ add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len)
  * blob where the data was stored.
  */
 static guint32
-add_to_blob (MonoAotCompile *acfg, guint8 *data, guint32 data_len)
+add_to_blob (MonoAotCompile *acfg, const guint8 *data, guint32 data_len)
 {
        if (acfg->blob.alloc_size == 0)
                stream_init (&acfg->blob);
@@ -1732,6 +1780,7 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8
                case MONO_WRAPPER_ALLOC: {
                        AllocatorWrapperInfo *info = mono_marshal_get_wrapper_info (method);
 
+                       /* The GC name is saved once in MonoAotFileInfo */
                        g_assert (info->alloc_type != -1);
                        encode_value (info->alloc_type, p, &p);
                        break;
@@ -3082,7 +3131,7 @@ emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg)
 
        method = cfg->orig_method;
        code = cfg->native_code;
-       header = mono_method_get_header (method);
+       header = cfg->header;
 
        method_index = get_method_index (acfg, method);
 
@@ -3416,7 +3465,7 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
 
        method = cfg->orig_method;
        code = cfg->native_code;
-       header = mono_method_get_header (method);
+       header = cfg->header;
 
        method_index = get_method_index (acfg, method);
 
@@ -3463,6 +3512,11 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
 
        /* Exception table */
        if (cfg->compile_llvm) {
+               /*
+                * When using LLVM, we can't emit some data, like pc offsets, this reg/offset etc.,
+                * since the information is only available to llc. Instead, we let llc save the data
+                * into the LSDA, and read it from there at runtime.
+                */
                /* The assembly might be CIL stripped so emit the data ourselves */
                if (header->num_clauses)
                        encode_value (header->num_clauses, p, &p);
@@ -3479,6 +3533,18 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
                        } else {
                                encode_value (0, p, &p);
                        }
+
+                       /* Emit a list of nesting clauses */
+                       for (i = 0; i < header->num_clauses; ++i) {
+                               gint32 cindex1 = k;
+                               MonoExceptionClause *clause1 = &header->clauses [cindex1];
+                               gint32 cindex2 = i;
+                               MonoExceptionClause *clause2 = &header->clauses [cindex2];
+
+                               if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset)
+                                       encode_value (i, p, &p);
+                       }
+                       encode_value (-1, p, &p);
                }
        } else {
                if (jinfo->num_clauses)
@@ -3510,9 +3576,11 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
        if (jinfo->has_generic_jit_info) {
                MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (jinfo);
 
-               encode_value (gi->has_this ? 1 : 0, p, &p);
-               encode_value (gi->this_reg, p, &p);
-               encode_value (gi->this_offset, p, &p);
+               if (!cfg->compile_llvm) {
+                       encode_value (gi->has_this ? 1 : 0, p, &p);
+                       encode_value (gi->this_reg, p, &p);
+                       encode_value (gi->this_offset, p, &p);
+               }
 
                /* 
                 * Need to encode jinfo->method too, since it is not equal to 'method'
@@ -3579,8 +3647,19 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token)
        gboolean no_special_static, cant_encode;
        gpointer iter = NULL;
 
-       if (!klass)
-               return add_to_blob (acfg, NULL, 0);
+       if (!klass) {
+               buf_size = 16;
+
+               p = buf = g_malloc (buf_size);
+
+               /* Mark as unusable */
+               encode_value (-1, p, &p);
+
+               res = add_to_blob (acfg, buf, p - buf);
+               g_free (buf);
+
+               return res;
+       }
                
        buf_size = 10240 + (klass->vtable_size * 16);
        p = buf = g_malloc (buf_size);
@@ -3783,13 +3862,17 @@ emit_trampoline (MonoAotCompile *acfg, int got_offset, MonoTrampInfo *info)
        ji = info->ji;
        unwind_ops = info->unwind_ops;
 
+#ifdef __native_client_codegen__
+       mono_nacl_fix_patches (code, ji);
+#endif
+
        /* Emit code */
 
        sprintf (start_symbol, "%s", name);
 
        emit_section_change (acfg, ".text", 0);
        emit_global (acfg, start_symbol, TRUE);
-       emit_alignment (acfg, 16);
+       emit_alignment (acfg, AOT_FUNC_ALIGNMENT);
        emit_label (acfg, start_symbol);
 
        sprintf (symbol, "%snamed_%s", acfg->temp_prefix, name);
@@ -3988,7 +4071,7 @@ emit_trampolines (MonoAotCompile *acfg)
                        }
 
                        emit_global (acfg, symbol, TRUE);
-                       emit_alignment (acfg, 16);
+                       emit_alignment (acfg, AOT_FUNC_ALIGNMENT);
                        emit_label (acfg, symbol);
 
                        acfg->trampoline_got_offset_base [ntype] = tramp_got_offset;
@@ -4012,6 +4095,10 @@ emit_trampolines (MonoAotCompile *acfg)
                                default:
                                        g_assert_not_reached ();
                                }
+#ifdef __native_client_codegen__
+                               /* align to avoid 32-byte boundary crossings */
+                               emit_alignment (acfg, AOT_FUNC_ALIGNMENT);
+#endif
 
                                if (!acfg->trampoline_size [ntype]) {
                                        g_assert (tramp_size);
@@ -4686,12 +4773,13 @@ emit_llvm_file (MonoAotCompile *acfg)
         * a lot of time, and doesn't seem to save much space.
         * The following optimizations cannot be enabled:
         * - 'tailcallelim'
+        * - 'jump-threading' changes our blockaddress references to int constants.
         * The opt list below was produced by taking the output of:
         * llvm-as < /dev/null | opt -O2 -disable-output -debug-pass=Arguments
         * 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 -jump-threading -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 -jump-threading -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 -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");
 #if 1
        command = g_strdup_printf ("opt -f %s -o temp.opt.bc temp.bc", opts);
        printf ("Executing opt: %s\n", command);
@@ -4787,6 +4875,9 @@ emit_code (MonoAotCompile *acfg)
                        }
 
                        emit_section_change (acfg, ".text", 0);
+#ifdef __native_client_codegen__
+                       emit_alignment (acfg, AOT_FUNC_ALIGNMENT);
+#endif
                        emit_global (acfg, symbol, TRUE);
                        emit_label (acfg, symbol);
 
@@ -5093,7 +5184,7 @@ emit_extra_methods (MonoAotCompile *acfg)
                if (!cfg)
                        continue;
 
-               buf_size = 1024;
+               buf_size = 10240;
                p = buf = g_malloc (buf_size);
 
                nmethods ++;
@@ -5659,7 +5750,7 @@ emit_globals (MonoAotCompile *acfg)
                 * Emit a global symbol which can be passed by an embedding app to
                 * mono_aot_register_module ().
                 */
-#if defined(__MACH__)
+#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);
@@ -5719,6 +5810,15 @@ emit_file_info (MonoAotCompile *acfg)
 {
        char symbol [128];
        int i;
+       int gc_name_offset;
+       const char *gc_name;
+
+       /*
+        * The managed allocators are GC specific, so can't use an AOT image created by one GC
+        * in another.
+        */
+       gc_name = mono_gc_get_gc_name ();
+       gc_name_offset = add_to_blob (acfg, (guint8*)gc_name, strlen (gc_name) + 1);
 
        sprintf (symbol, "mono_aot_file_info");
        emit_section_change (acfg, ".data", 0);
@@ -5726,13 +5826,14 @@ emit_file_info (MonoAotCompile *acfg)
        emit_label (acfg, symbol);
        emit_global (acfg, symbol, FALSE);
 
-       /* The data emitted here must match MonoAotFileInfo in aot-runtime.c. */
+       /* The data emitted here must match MonoAotFileInfo. */
        emit_int32 (acfg, acfg->plt_got_offset_base);
        emit_int32 (acfg, (int)(acfg->got_offset * sizeof (gpointer)));
        emit_int32 (acfg, acfg->plt_offset);
        emit_int32 (acfg, acfg->nmethods);
        emit_int32 (acfg, acfg->flags);
        emit_int32 (acfg, acfg->opts);
+       emit_int32 (acfg, gc_name_offset);
 
        for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
                emit_int32 (acfg, acfg->num_trampolines [i]);
@@ -5915,6 +6016,12 @@ compile_asm (MonoAotCompile *acfg)
 #define AS_OPTIONS ""
 #endif
 
+#ifdef __native_client_codegen__
+#define AS_NAME "nacl-as"
+#else
+#define AS_NAME "as"
+#endif
+
 #ifndef LD_OPTIONS
 #define LD_OPTIONS ""
 #endif
@@ -5940,7 +6047,7 @@ compile_asm (MonoAotCompile *acfg)
        } else {
                objfile = g_strdup_printf ("%s.o", acfg->tmpfname);
        }
-       command = g_strdup_printf ("%sas %s %s -o %s", tool_prefix, AS_OPTIONS, acfg->tmpfname, objfile);
+       command = g_strdup_printf ("%s%s %s %s -o %s", tool_prefix, AS_NAME, AS_OPTIONS, acfg->tmpfname, objfile);
        printf ("Executing the native assembler: %s\n", command);
        if (system (command) != 0) {
                g_free (command);