Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT][mono][aot] Implementation of nollvm init method #89074

Closed
wants to merge 17 commits into from

Conversation

LeVladIonescu
Copy link
Contributor

This PR aims to introduce the implementation of method's self initialization for nollvm cases, firstly on Arm64.
This won't be enabled since it's not tested yet.

Addresses #82224.

Vlad - Alexandru Ionescu and others added 6 commits June 30, 2023 12:27
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MBP-5.guest.corp.microsoft.com>
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
@LeVladIonescu LeVladIonescu added NO-REVIEW Experimental/testing PR, do NOT review it area-Codegen-AOT-mono labels Jul 18, 2023
@LeVladIonescu LeVladIonescu self-assigned this Jul 18, 2023
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
@LeVladIonescu LeVladIonescu changed the title [DRAFT][mono][aot] Implementation of nollvm init method [mono][aot] Implementation of nollvm init method Jul 18, 2023
@LeVladIonescu LeVladIonescu removed the NO-REVIEW Experimental/testing PR, do NOT review it label Jul 18, 2023
@LeVladIonescu
Copy link
Contributor Author

@vargaz @lambdageek This is ready for review, I've enabled the optimization just to see how it behaves on CI.
cc @kotlarmilos

Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
mono_bitset_set (inited_bitset, method_index);
}
}
mono_error_assert_ok (error);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vargaz do we want to propagate this error?

Copy link
Member

@lambdageek lambdageek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I completely followed what is going on. but the marshal stuff looks ok. I had some minor suggestions for method-to-ir and the arm64 backend. And a question about why this is backend-specific.

Comment on lines 11261 to 11267
case MONO_CEE_AOT_MODULE: {
if (cfg->compile_aot) {
EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_AOT_MODULE, NULL);
*sp++ = ins;
}
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't expect to see this opcode if we're JITing, right? can we do g_assert (cfg->compile_aot)?
Alternately, can we add an else case that pushes some dummy constant (NULL?) on the stack. That way even if we emit this during JITing by accident it will just mean we'll see a NULL module in the icall instead of getting a messed up operand stack in the wrapper method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll go with the first approach, since the second will also need a NULL check for the amodule inside the call.

Comment on lines 1053 to 1060
emit_call (MonoCompile *cfg, guint8* code, MonoJumpInfoType patch_type, gconstpointer data, MonoMethod *method)
{
/*
mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_ARM64_IMM);
code = emit_imm64_template (code, ARMREG_LR);
arm_blrx (code, ARMREG_LR);
*/
mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_ARM64_BL);
mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, method ? method : data, MONO_R_ARM64_BL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessary - MonoMethod* is a pointer-sized value. You can just pass it in for the data argument (possibly with a cast to deal with the const-ness).

src/mono/mono/mini/mini-arm64.c Outdated Show resolved Hide resolved
src/mono/mono/metadata/marshal.c Outdated Show resolved Hide resolved
src/mono/mono/mini/aot-compiler.c Outdated Show resolved Hide resolved
/*
* Methods init bitset used for initialization during the runtime
*/
amodule->mono_inited = mono_bitset_new (amodule->info.nmethods, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed offline, would be good to think of memory leaks. Is it allocated within the mempool?

@LeVladIonescu
Copy link
Contributor Author

I'm not sure I completely followed what is going on. but the marshal stuff looks ok. I had some minor suggestions for method-to-ir and the arm64 backend. And a question about why this is backend-specific.

The main idea is to have this aot_get_init_wrapper being called from the method's prolog which will check (for nollvm cases) if the method is already initialised or not. If it is not, then a callback and a call will be used to call init_method (i.e. mini_nollvm_init_method).
This is targeting only arm64 at the moment due to testing purposes, the plan is to enable it on all platforms.

@lambdageek
Copy link
Member

I'm not sure I completely followed what is going on. but the marshal stuff looks ok. I had some minor suggestions for method-to-ir and the arm64 backend. And a question about why this is backend-specific.

The main idea is to have this aot_get_init_wrapper being called from the method's prolog which will check (for nollvm cases) if the method is already initialised or not. If it is not, then a callback and a call will be used to call init_method (i.e. mini_nollvm_init_method). This is targeting only arm64 at the moment due to testing purposes, the plan is to enable it on all platforms.

got it. thanks. are we planning to use this by default? or only if we enable some direct call aot option? This is introducing slow overhead on every method call, right?

If I call _SomeAssembly_SomeClass_SomeMethod() the first thing it will do every time is bl _aot_init_wrapper right? So if I call SomeMethod in a loop 10000 times, we will call the init wrapper 10000 times and after the first call it will always just immediately return?

For a follow-up PR, is there some way we can emit an inlined check of the bitmask? so that in the fast path we're just doing a memory load and avoiding a call from the prolog of every method?

@vargaz
Copy link
Contributor

vargaz commented Jul 19, 2023

So this is an experiment to see whenever this can be made to work and what the perf impact woudl be. In theory, it can be made as fast as the llvm version, which does:
if (!bitmap[idx]) init method (idx)
The init method should also have the 'cold' calling convention in the future, which means it doesn't clobber any registers, beside the one used to pass idx.

The upside is that these methods can now be called directly, the caller doesn't have to go through a PLT entry. So the method becomes a little slower, but calling it becomes a little cheaper.

Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Copy link
Member

@lambdageek lambdageek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. I'm not sure about ENABLE_LLVM - that just means whether the AOT compiler has LLVM support built-in - not that the LLVM support is being used for the current compilation - so I'm not sure that's the condition for deciding what does into the wrapper method.

src/mono/mono/mini/aot-compiler.c Show resolved Hide resolved
src/mono/mono/mini/mini-arm64.c Outdated Show resolved Hide resolved
src/mono/mono/mini/aot-compiler.c Outdated Show resolved Hide resolved
src/mono/mono/mini/aot-compiler.c Outdated Show resolved Hide resolved
Comment on lines 4393 to 4396
#ifdef ENABLE_WIP_METHOD_NOLLVM_SELF_INIT
else
mono_bitset_set (mono_aot_get_mono_inited(amodule), method_index);
#endif
Copy link
Member

@lambdageek lambdageek Jul 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you used to have an arm64 target check here (and also that LLVM was disabled). but now you don't.

Do you need to enable ENABLE_WIP_METHOD_NOLLVM_SELF_INIT in src/mono/CMakeLists.txt if we're targeting arm64?

Comment on lines 2982 to 2984
#ifndef ENABLE_LLVM
get_marshal_cb ()->emit_method_init (mb);
#endif
Copy link
Member

@lambdageek lambdageek Jul 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make sense to me. ENABLE_LLVM just means we're building the runtime with LLVM support. It doesn't mean that we're using LLVM for the current AOT compilation. I think you need two different wrapper subtypes AOT_INIT_METHOD_LLVM and AOT_INIT_METHOD_NOLLVM and at the callsite you would do something like mono_marshal_get_aot_init_wrapper (cfg->compile_aot && COMPILE_LLVM(cfg) ? AOT_INIT_METHOD_LLVM : AOT_INIT_METHOD_NOLLVM)

Vlad - Alexandru Ionescu and others added 3 commits July 21, 2023 17:20
Signed-off-by: Vlad - Alexandru Ionescu <vlad-alexandruionescu@Vlads-MacBook-Pro-5.local>
Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>
Signed-off-by: Vlad <vladionescu1399@gmail.com>
@LeVladIonescu LeVladIonescu changed the title [mono][aot] Implementation of nollvm init method [DRAFT][mono][aot] Implementation of nollvm init method Aug 17, 2023
@LeVladIonescu LeVladIonescu added the NO-REVIEW Experimental/testing PR, do NOT review it label Aug 17, 2023
@LeVladIonescu
Copy link
Contributor Author

Converting this PR to draft in order to test experimental changes on arm64.
These action points need to be solved in order to merge it first for arm64:

  • make it work on the sample app
  • runtime working properly with everything inited
  • CI green

cc @kotlarmilos @SamMonoRT

#ifdef ENABLE_WIP_METHOD_NOLLVM_SELF_INIT
if (cfg->compile_aot && !COMPILE_LLVM (cfg)) {
code = emit_imm (code, ARMREG_R0, cfg->method_index);
code = emit_call (cfg, code, MONO_PATCH_INFO_METHOD, (gconstpointer) mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is a correct way to invoke wrappers generated in:

add_method (acfg, mono_marshal_get_aot_init_wrapper ((MonoAotInitSubtype)i));

During the native linking, there is an undefined symbols for architecture arm64:

  "_mono_aot_HelloWorld_init_method", referenced from:
      HelloWorld_Program_Main_string__ in mono_aot_0nLeOF.o
      HelloWorld_Program__ctor in mono_aot_0nLeOF.o
      wrapper_other_object_init_method_intptr_intptr in mono_aot_0nLeOF.o
      wrapper_other_object_init_method_gshared_mrgctx_intptr_intptr_intptr in mono_aot_0nLeOF.o
      wrapper_other_object_init_method_gshared_this_intptr_intptr_intptr in mono_aot_0nLeOF.o
      wrapper_other_object_init_method_gshared_vtable_intptr_intptr_intptr in mono_aot_0nLeOF.o

I think it comes from method prolog trying to invoke the init wrapper, but I would expect invocation of wrapper_other_object_init_method_intptr_intptr instead of _mono_aot_HelloWorld_init_method. @vargaz What we are doing wrong?

Copy link
Member

@ivanpovazan ivanpovazan Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I am not 100% how this should work but I think the body of _mono_aot_HelloWorld_init_method is never generated, looking at the compiler output: https://gist.github.com/ivanpovazan/96542ae24088a4a5e735d414b92e4c9e
there are only jumps to that symbol bl _mono_aot_HelloWorld_init_method, but it is not generated anywhere.

Signed-off-by: Vlad <vladionescu1399@gmail.com>
@LeVladIonescu
Copy link
Contributor Author

In the current state, the problem with the symbol of the init wrapper persists. There is a mismatch between what is emitted in the non llvm case and what symbol is the init wrapper trying to call.

@ghost
Copy link

ghost commented Nov 30, 2023

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@github-actions github-actions bot locked and limited conversation to collaborators Dec 30, 2023
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Codegen-AOT-mono NO-REVIEW Experimental/testing PR, do NOT review it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants