Skip to content

Commit

Permalink
[wasm] Bitcode code generation improvements (mono/mono#11413)
Browse files Browse the repository at this point in the history
* [llvm] Rewrite the bitcode get_method () generated method to load the result from a table on wasm instead of a huge
switch statement.

This might be usable on other platforms in the future but having a table full of function pointers might lead to linking
problems.

* [llvm] Make direct calls to jit icalls without wrappers as well.

* [llvm] Export mono_llvm_clear_exception () with G_EXTERN_C, its called from llvm generated code.

* [wasm] Emit target layout/target triple into bitcode files.

* [aot] Allow the decoding of references to icall wrappers, so compiled code can reference them directly using a MONO_PATCH_INFO_METHOD.

* [llvm] Call icall wrappers directly from corlib code in llvmonly mode instead of going through the GOT.

* [llvm] Instance methods without got accesses don't need initialization in llvmonly mode.


Commit migrated from mono/mono@c0da2a5
  • Loading branch information
vargaz authored Oct 29, 2018
1 parent 273ef0e commit f4c42af
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 69 deletions.
13 changes: 11 additions & 2 deletions src/mono/mono/mini/aot-compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -3501,8 +3501,11 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8
g_assert (info);
encode_value (info->subtype, p, &p);
if (info->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) {
strcpy ((char*)p, method->name);
p += strlen (method->name) + 1;
MonoJitICallInfo *callinfo = NULL;
callinfo = mono_find_jit_icall_by_addr (info->d.icall.func);
g_assert (callinfo);
strcpy ((char*)p, callinfo->name);
p += strlen (callinfo->name) + 1;
} else if (info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT) {
encode_method_ref (acfg, info->d.managed_to_native.method, p, &p);
} else {
Expand Down Expand Up @@ -7834,6 +7837,7 @@ can_encode_method (MonoAotCompile *acfg, MonoMethod *method)
case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE:
case MONO_WRAPPER_DELEGATE_END_INVOKE:
case MONO_WRAPPER_SYNCHRONIZED:
case MONO_WRAPPER_MANAGED_TO_NATIVE:
break;
case MONO_WRAPPER_MANAGED_TO_MANAGED:
case MONO_WRAPPER_CASTCLASS: {
Expand Down Expand Up @@ -9078,6 +9082,11 @@ mono_aot_get_direct_call_symbol (MonoJumpInfoType type, gconstpointer data)
sym = mono_lookup_icall_symbol (method);
else if (llvm_acfg->aot_opts.direct_pinvoke)
sym = get_pinvoke_import (llvm_acfg, method);
} else if (type == MONO_PATCH_INFO_INTERNAL_METHOD) {
MonoJitICallInfo *info = mono_find_jit_icall_by_name ((const char*)data);
const char *name = mono_lookup_jit_icall_symbol ((const char*)data);
if (name && llvm_acfg->aot_opts.direct_icalls && info->func == info->wrapper)
sym = name;
}
if (sym)
return g_strdup (sym);
Expand Down
15 changes: 7 additions & 8 deletions src/mono/mono/mini/aot-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1129,13 +1129,11 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
char *name;

if (subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) {
if (!target)
return FALSE;

name = (char*)p;
if (strcmp (target->name, name) != 0)
return FALSE;
ref->method = target;

MonoJitICallInfo *info = mono_find_jit_icall_by_name (name);
g_assert (info);
ref->method = mono_icall_get_wrapper_method (info);
} else {
m = decode_resolve_method_ref (module, p, &p, error);
if (!m)
Expand Down Expand Up @@ -4548,9 +4546,10 @@ static void
init_llvmonly_method (MonoAotModule *amodule, guint32 method_index, MonoMethod *method, MonoClass *init_class, MonoGenericContext *context)
{
ERROR_DECL (error);
gboolean res;

init_method (amodule, method_index, method, init_class, context, error);
if (!is_ok (error)) {
res = init_method (amodule, method_index, method, init_class, context, error);
if (!res || !is_ok (error)) {
MonoException *ex = mono_error_convert_to_exception (error);
/* Its okay to raise in llvmonly mode */
if (ex)
Expand Down
164 changes: 128 additions & 36 deletions src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,7 @@ get_callee (EmitContext *ctx, LLVMTypeRef llvm_sig, MonoJumpInfoType type, gcons
{
LLVMValueRef callee;
char *callee_name;

if (ctx->llvm_only) {
callee_name = mono_aot_get_direct_call_symbol (type, data);
if (callee_name) {
Expand All @@ -1723,10 +1724,39 @@ get_callee (EmitContext *ctx, LLVMTypeRef llvm_sig, MonoJumpInfoType type, gcons
return callee;
}

/*
* Change references to jit icalls to the icall wrappers when in corlib, so
* they can be called directly.
*/
if (ctx->module->assembly->image == mono_get_corlib () && type == MONO_PATCH_INFO_INTERNAL_METHOD) {
MonoJitICallInfo *info = mono_find_jit_icall_by_name ((const char*)data);
g_assert (info);

if (info->func != info->wrapper) {
type = MONO_PATCH_INFO_METHOD;
data = mono_icall_get_wrapper_method (info);
}
}

/*
* Calls are made through the GOT.
*/
return get_aotconst_typed (ctx, type, data, LLVMPointerType (llvm_sig, 0));
LLVMValueRef callee = get_aotconst_typed (ctx, type, data, LLVMPointerType (llvm_sig, 0));

if (type == MONO_PATCH_INFO_METHOD) {
MonoMethod *method = (MonoMethod*)data;
if (m_class_get_image (method->klass)->assembly == ctx->module->assembly) {
/*
* Collect instructions representing the callee into a hash so they can be replaced
* by the llvm method for the callee if the callee turns out to be direct
* callable. Currently this only requires it to not fail llvm compilation.
*/
GSList *l = (GSList*)g_hash_table_lookup (ctx->method_to_callers, method);
l = g_slist_prepend (l, callee);
g_hash_table_insert (ctx->method_to_callers, method, l);
}
}
return callee;
} else {
MonoJumpInfo *ji = NULL;

Expand Down Expand Up @@ -2477,17 +2507,49 @@ emit_get_method (MonoLLVMModule *module)
LLVMModuleRef lmodule = module->lmodule;
LLVMValueRef func, switch_ins, m;
LLVMBasicBlockRef entry_bb, fail_bb, bb, code_start_bb, code_end_bb;
LLVMBasicBlockRef *bbs;
LLVMBasicBlockRef *bbs = NULL;
LLVMTypeRef rtype;
LLVMBuilderRef builder = LLVMCreateBuilder ();
LLVMValueRef table = NULL;
char *name;
int i;
gboolean emit_table = FALSE;

#ifdef TARGET_WASM
/*
* Emit a table of functions instead of a switch statement,
* its very efficient on wasm. This might be usable on
* other platforms too.
*/
emit_table = TRUE;
#endif

rtype = LLVMPointerType (LLVMInt8Type (), 0);

if (emit_table) {
LLVMTypeRef table_type;
LLVMValueRef *table_elems;
char *table_name;

int table_len = module->max_method_idx + 1;
table_type = LLVMArrayType (rtype, table_len);
table_name = g_strdup_printf ("%s_method_table", module->assembly->aname.name);
table = LLVMAddGlobal (lmodule, table_type, table_name);
table_elems = g_new0 (LLVMValueRef, table_len);
for (i = 0; i < table_len; ++i) {
m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_lmethod, GINT_TO_POINTER (i));
if (m)
table_elems [i] = LLVMBuildBitCast (builder, m, rtype, "");
else
table_elems [i] = LLVMConstNull (rtype);
}
LLVMSetInitializer (table, LLVMConstArray (LLVMPointerType (LLVMInt8Type (), 0), table_elems, table_len));
}

/*
* Emit a switch statement. Emitting a table of function addresses is smaller/faster,
* but generating code seems safer.
*/
rtype = LLVMPointerType (LLVMInt8Type (), 0);
func = LLVMAddFunction (lmodule, module->get_method_symbol, LLVMFunctionType1 (rtype, LLVMInt32Type (), FALSE));
LLVMSetLinkage (func, LLVMExternalLinkage);
LLVMSetVisibility (func, LLVMHiddenVisibility);
Expand All @@ -2514,33 +2576,57 @@ emit_get_method (MonoLLVMModule *module)
LLVMPositionBuilderAtEnd (builder, code_end_bb);
LLVMBuildRet (builder, LLVMBuildBitCast (builder, module->code_end, rtype, ""));

bbs = g_new0 (LLVMBasicBlockRef, module->max_method_idx + 1);
for (i = 0; i < module->max_method_idx + 1; ++i) {
name = g_strdup_printf ("BB_%d", i);
bb = LLVMAppendBasicBlock (func, name);
g_free (name);
bbs [i] = bb;
if (emit_table) {
/*
* switch (index) {
* case -1: return code_start;
* case -2: return code_end;
* default: return method_table [index];
*/
LLVMBasicBlockRef default_bb = LLVMAppendBasicBlock (func, "DEFAULT");
LLVMPositionBuilderAtEnd (builder, default_bb);
LLVMValueRef base = table;
LLVMValueRef indexes [2];
indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
indexes [1] = LLVMGetParam (func, 0);
LLVMValueRef addr = LLVMBuildGEP (builder, base, indexes, 2, "");
LLVMValueRef res = mono_llvm_build_load (builder, addr, "", FALSE);
LLVMBuildRet (builder, res);

LLVMPositionBuilderAtEnd (builder, entry_bb);

switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), default_bb, 0);
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -1, FALSE), code_start_bb);
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -2, FALSE), code_end_bb);
} else {
bbs = g_new0 (LLVMBasicBlockRef, module->max_method_idx + 1);
for (i = 0; i < module->max_method_idx + 1; ++i) {
name = g_strdup_printf ("BB_%d", i);
bb = LLVMAppendBasicBlock (func, name);
g_free (name);
bbs [i] = bb;

LLVMPositionBuilderAtEnd (builder, bb);
LLVMPositionBuilderAtEnd (builder, bb);

m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_lmethod, GINT_TO_POINTER (i));
if (m)
LLVMBuildRet (builder, LLVMBuildBitCast (builder, m, rtype, ""));
else
LLVMBuildRet (builder, LLVMConstNull (rtype));
}
m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_lmethod, GINT_TO_POINTER (i));
if (m)
LLVMBuildRet (builder, LLVMBuildBitCast (builder, m, rtype, ""));
else
LLVMBuildRet (builder, LLVMConstNull (rtype));
}

fail_bb = LLVMAppendBasicBlock (func, "FAIL");
LLVMPositionBuilderAtEnd (builder, fail_bb);
LLVMBuildRet (builder, LLVMConstNull (rtype));
fail_bb = LLVMAppendBasicBlock (func, "FAIL");
LLVMPositionBuilderAtEnd (builder, fail_bb);
LLVMBuildRet (builder, LLVMConstNull (rtype));

LLVMPositionBuilderAtEnd (builder, entry_bb);
LLVMPositionBuilderAtEnd (builder, entry_bb);

switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), fail_bb, 0);
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -1, FALSE), code_start_bb);
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -2, FALSE), code_end_bb);
for (i = 0; i < module->max_method_idx + 1; ++i) {
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i, FALSE), bbs [i]);
switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), fail_bb, 0);
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -1, FALSE), code_start_bb);
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -2, FALSE), code_end_bb);
for (i = 0; i < module->max_method_idx + 1; ++i) {
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i, FALSE), bbs [i]);
}
}

mark_as_used (module, func);
Expand Down Expand Up @@ -3309,16 +3395,6 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
set_failure (ctx, "can't encode patch");
return;
}
if (cfg->llvm_only && m_class_get_image (call->method->klass)->assembly == ctx->module->assembly) {
/*
* Collect instructions representing the callee into a hash so they can be replaced
* by the llvm method for the callee if the callee turns out to be direct
* callable. Currently this only requires it to not fail llvm compilation.
*/
GSList *l = (GSList*)g_hash_table_lookup (ctx->method_to_callers, call->method);
l = g_slist_prepend (l, callee);
g_hash_table_insert (ctx->method_to_callers, call->method, l);
}
} else {
ERROR_DECL (error);
static int tramp_index;
Expand Down Expand Up @@ -7576,7 +7652,15 @@ emit_method_inner (EmitContext *ctx)
* NATIVE_TO_MANAGED methods might be called on a thread not attached to the runtime, so they are initialized when loaded
* in load_method ().
*/
if ((ctx->has_got_access || mono_class_get_cctor (cfg->method->klass)) && !(cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)) {
gboolean needs_init = ctx->has_got_access;
if (!needs_init && mono_class_get_cctor (cfg->method->klass)) {
/* Needs init to run the cctor */
if (cfg->method->flags & METHOD_ATTRIBUTE_STATIC)
needs_init = TRUE;
}
if (cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
needs_init = FALSE;
if (needs_init) {
/*
* linkonce methods shouldn't have initialization,
* because they might belong to assemblies which
Expand Down Expand Up @@ -8742,6 +8826,14 @@ mono_llvm_create_aot_module (MonoAssembly *assembly, const char *global_prefix,
add_intrinsics (module->lmodule);
add_types (module);

#ifdef MONO_ARCH_LLVM_TARGET_LAYOUT
LLVMSetDataLayout (module->lmodule, MONO_ARCH_LLVM_TARGET_LAYOUT);
#endif

#ifdef MONO_ARCH_LLVM_TARGET_TRIPLE
LLVMSetTarget (module->lmodule, MONO_ARCH_LLVM_TARGET_TRIPLE);
#endif

#if LLVM_API_VERSION > 100
if (module->emit_dwarf) {
char *dir, *build_info, *s, *cu_name;
Expand Down
29 changes: 19 additions & 10 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,35 +607,44 @@ mono_debug_count (void)
return TRUE;
}

MonoMethod*
mono_icall_get_wrapper_method (MonoJitICallInfo* callinfo)
{
MonoMethod *wrapper;
gboolean check_exc = TRUE;
char *name;

if (!strcmp (callinfo->name, "mono_thread_interruption_checkpoint"))
/* This icall is used to check for exceptions, so don't check in the wrapper */
check_exc = FALSE;

name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name);
wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func, check_exc);
g_free (name);

return wrapper;
}

gconstpointer
mono_icall_get_wrapper_full (MonoJitICallInfo* callinfo, gboolean do_compile)
{
ERROR_DECL (error);
char *name;
MonoMethod *wrapper;
gconstpointer trampoline;
MonoDomain *domain = mono_get_root_domain ();
gboolean check_exc = TRUE;

if (callinfo->wrapper)
return callinfo->wrapper;

if (callinfo->trampoline)
return callinfo->trampoline;

if (!strcmp (callinfo->name, "mono_thread_interruption_checkpoint"))
/* This icall is used to check for exceptions, so don't check in the wrapper */
check_exc = FALSE;

name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name);
wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func, check_exc);
g_free (name);
wrapper = mono_icall_get_wrapper_method (callinfo);

if (do_compile) {
trampoline = mono_compile_method_checked (wrapper, error);
mono_error_assert_ok (error);
} else {

trampoline = mono_create_jit_trampoline (domain, wrapper, error);
mono_error_assert_ok (error);
trampoline = mono_create_ftnptr (domain, (gpointer)trampoline);
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/mini-runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ MONO_API void mono_jit_set_domain (MonoDomain *domain);

gboolean mono_method_same_domain (MonoJitInfo *caller, MonoJitInfo *callee);
gpointer mono_create_ftnptr (MonoDomain *domain, gpointer addr);
MonoMethod* mono_icall_get_wrapper_method (MonoJitICallInfo* callinfo) MONO_LLVM_INTERNAL;
gconstpointer mono_icall_get_wrapper (MonoJitICallInfo* callinfo) MONO_LLVM_INTERNAL;
gconstpointer mono_icall_get_wrapper_full (MonoJitICallInfo* callinfo, gboolean do_compile);

Expand Down
24 changes: 12 additions & 12 deletions src/mono/mono/mini/mini-wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,18 +293,6 @@ mono_arch_flush_icache (guint8 *code, gint size)
{
}

const char*
mono_arch_fregname (int reg)
{
return "freg0";
}

const char*
mono_arch_regname (int reg)
{
return "r0";
}

LLVMCallInfo*
mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
{
Expand Down Expand Up @@ -356,6 +344,18 @@ mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig,

#endif // DISABLE_JIT

const char*
mono_arch_fregname (int reg)
{
return "freg0";
}

const char*
mono_arch_regname (int reg)
{
return "r0";
}

int
mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
{
Expand Down
Loading

0 comments on commit f4c42af

Please sign in to comment.