Point where code installed by C1 and C2 is loaded?

Vladimir Ivanov vladimir.x.ivanov at oracle.com
Thu Oct 13 17:12:59 UTC 2022


> Looking at set_code it seems to me like the redirection is done in
> 
> mh->_from_compiled_entry = code->verified_entry_point();
> 
> while
> 
> mh->_from_interpreted_entry = mh->get_i2c_entry();
> 
> is done to make execution jump to compiled code if it was running in the 
> interpreter prior, did i get that right? Seems a little strange to me 
> that _code is also stored in the Method if it's not actually used in 
> execution, but I guess it's probably used for other reasons. Given the 

Method::code() is extensively used to get the nmethod associated with 
the particular Method instance. It's possible to derive the same 
information from Method::_from_compiled_entry, but it's much easier to 
just keep a pointer to the nmethod itself.


> comments accompanying _from_compiled_entry and _from_interpreted_entry:
> 
> // Entry point for calling from compiled code, to compiled code if it exists
> // or else the interpreter.
> 
> // Cache of _code ? _adapter->i2c_entry() : _i2i_entry
> // Cache of: _code ? _code->entry_point() : _adapter->c2i_entry()
> 
> If i understand this correctly, if _code != NULL, 
> _from_interpreted_entry would be _adapter->i2c_entry(), which is(?) a 
> CodeBlob containing assembly to transfer control to the associated 
> nmethod, and vice versa for _from_compiled_entry? I am somewhat curious 
> about where these are actually loaded during frame creation, and how the 
> runtime would know whether what it just received were the contents of 
> an nmethod that could be executed immediately, or a method running in 
> the interpreter that needs to be decoded, dispatched, etc.

I'm not sure I fully understood your question.

Interpreter and compiled code have different calling conventions, that's 
the reason why they use distinct entry points. i2c/c2i are frameless 
adapters which perform conversion between those calling conventions as 
part of invocation. See SharedRuntime::generate_i2c2i_adapters() for 
details [1].

Best regards,
Vladimir Ivanov


[1] 
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/sharedRuntime.hpp#L394-L426

> 
> I appreciate the response explaining method invocation!
> 
> best regards,
> Julian
> 
> On Thu, Oct 13, 2022 at 1:13 AM Vladimir Ivanov 
> <vladimir.x.ivanov at oracle.com <mailto:vladimir.x.ivanov at oracle.com>> wrote:
> 
>     At runtime, every method is invoked either through
>     Method::_from_compiled_entry or Method::_from_interpreted_entry
>     (depending on where the call is performed from).
> 
>     As part of nmethod installation (ciEnv::register_method()), entry
>     points
>     of the relevant method (the root of the compilation) are updated (by
>     Method::set_code()) and Method::_from_compiled_entry starts to point to
>     the entry point of the freshly installed nmethod.
> 
>     (Also, there is a special case of OSR compilation, but I don't cover it
>     here.)
> 
>     src/hotspot/share/oops/method.hpp [1]:
> 
>     class Method : public Metadata {
>     ...
>         // Entry point for calling from compiled code, to compiled code
>     if it
>     exists
>         // or else the interpreter.
>         volatile address _from_compiled_entry;        // Cache of: _code ?
>     _code->entry_point() : _adapter->c2i_entry()
>         // The entry point for calling both from and to compiled code is
>         // "_code->entry_point()".  Because of tiered compilation and
>     de-opt,
>     this
>         // field can come and go.  It can transition from NULL to
>     not-null at any
>         // time (whenever a compile completes).  It can transition from
>     not-null to
>         // NULL only at safepoints (because of a de-opt).
>         CompiledMethod* volatile _code;                       // Points to
>     the corresponding piece of native code
>         volatile address           _from_interpreted_entry; // Cache of
>     _code
>     ? _adapter->i2c_entry() : _i2i_entry
> 
>     Hp
> 
>     Best regards,
>     Vladimir Ivanov
> 
>     [1]
>     https://github.com/openjdk/jdk/blob/master/src/hotspot/share/oops/method.hpp#L109-L118 <https://urldefense.com/v3/__https://github.com/openjdk/jdk/blob/master/src/hotspot/share/oops/method.hpp*L109-L118__;Iw!!ACWV5N9M2RV99hQ!MUt3I6g-RaNuCNWRFRx0pelLvi4jGEvnVQw2OwfKU7V9di9xQwY3yDF30lqD8l7cNrqEYwuGB5m3gV1i3mPU6tXk7j8D-Q$>
> 
>     On 10/12/22 08:18, Julian Waters wrote:
>      > Hi all,
>      >
>      > I apologise if this is a silly question, but where exactly is code
>      > installed by C1 and C2 loaded and executed by the runtime? I've
>     tried
>      > looking through the entirety of hotspot, but haven't found
>     anything that
>      > seems to be related. All I can surmise is that the compiler
>     interface
>      > ultimately creates an nmethod that allocates itself on the CodeCache
>      > using a CodeBuffer containing C1 or C2 emitted instructions, and
>     then
>      > Method::set_code sets the method's _code to reference that entry
>     in the
>      > cache (or method->method_holder()->add_osr_nmethod(nm); is called in
>      > other circumstances, I don't quite understand what the difference
>     is but
>      > I assume the end result is probably the same). Given my rudimentary
>      > understanding of hotspot's execution pipeline I'd assume that
>     when a new
>      > frame (frame.hpp) is created, the frame's code blob would be set to
>      > reference the nmethod in the method that was called, or otherwise
>      > somehow jump back to the interpreter if that method hasn't been
>     compiled
>      > yet. But there doesn't seem to be any point where method->code() is
>      > called to load the instructions emitted by either C1 or C2 into a
>     frame,
>      > so where does that happen?
>      >
>      > I guess this is probably more a question of how hotspot runs loaded
>      > programs in general, which seems to me at a glance like it's
>     chaining
>      > assembly in CodeBlobs together and jumping to the next
>     blob/codelet (in
>      > the next frame?) when it's finished, but I can't really figure
>     out where
>      > those codelets are set for each frame, or how it chooses between one
>      > compiled by C1 or C2, and the handwritten assembly codelets that
>     make up
>      > the interpreter (or for that matter how it even finds the correct
>      > interpreter codelet).
>      >
>      > I appreciate any help with this query, sorry if this isn't the
>     correct
>      > list to post this question to, but it seemed like the most
>     appropriate.
>      >
>      > best regards,
>      > Julian
> 


More information about the hotspot-compiler-dev mailing list