Skip to content

Conversation

@vnkozlov
Copy link
Contributor

@vnkozlov vnkozlov commented Feb 9, 2025

Remove virtual methods from CodeBlob and nmethod to simplify saving/restoring in Leyden AOT cache. It avoids the need to patch hidden VPTR pointer to class's virtual table.

Added C++ static asserts to make sure no virtual methods are added in a future.

Fixed/cleaned SA code which process CodeBlob and its subclasses. Use CodeBlob::_kind field value to determine the type of blob.

Tested tier1-5, hs-tier6-rt (for JFR testing), stress, xcomp


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8349088: De-virtualize Codeblob and nmethod (Enhancement - P4)

Reviewers

Contributors

  • Stefan Karlsson <stefank@openjdk.org>
  • Chris Plummer <cjplummer@openjdk.org>

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/23533/head:pull/23533
$ git checkout pull/23533

Update a local copy of the PR:
$ git checkout pull/23533
$ git pull https://git.openjdk.org/jdk.git pull/23533/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 23533

View PR using the GUI difftool:
$ git pr show -t 23533

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/23533.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 9, 2025

👋 Welcome back kvn! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Feb 9, 2025

@vnkozlov This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8349088: De-virtualize Codeblob and nmethod

Co-authored-by: Stefan Karlsson <stefank@openjdk.org>
Co-authored-by: Chris Plummer <cjplummer@openjdk.org>
Reviewed-by: cjplummer, aboldtch, dlong

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 52 new commits pushed to the master branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the rfr Pull request is ready for review label Feb 9, 2025
@openjdk
Copy link

openjdk bot commented Feb 9, 2025

@vnkozlov The following labels will be automatically applied to this pull request:

  • hotspot
  • serviceability

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added serviceability serviceability-dev@openjdk.org hotspot hotspot-dev@openjdk.org labels Feb 9, 2025
@mlbridge
Copy link

mlbridge bot commented Feb 9, 2025

@vnkozlov
Copy link
Contributor Author

vnkozlov commented Feb 9, 2025

@dougxc and @tkrodriguez, please look if it affects Graal.

@vnkozlov
Copy link
Contributor Author

vnkozlov commented Feb 9, 2025

/cc graal

@vnkozlov
Copy link
Contributor Author

vnkozlov commented Feb 9, 2025

/cc hotspot-compiler

@openjdk openjdk bot added the graal graal-dev@openjdk.org label Feb 9, 2025
@openjdk
Copy link

openjdk bot commented Feb 9, 2025

@vnkozlov
The graal label was successfully added.

@openjdk openjdk bot added the hotspot-compiler hotspot-compiler-dev@openjdk.org label Feb 9, 2025
@openjdk
Copy link

openjdk bot commented Feb 9, 2025

@vnkozlov
The hotspot-compiler label was successfully added.

@vnkozlov
Copy link
Contributor Author

vnkozlov commented Feb 9, 2025

/cc hotspot-runtime

@vnkozlov
Copy link
Contributor Author

vnkozlov commented Feb 9, 2025

/label remove hotspot

@openjdk openjdk bot added the hotspot-runtime hotspot-runtime-dev@openjdk.org label Feb 9, 2025
@openjdk
Copy link

openjdk bot commented Feb 9, 2025

@vnkozlov
The hotspot-runtime label was successfully added.

@openjdk openjdk bot removed the hotspot hotspot-dev@openjdk.org label Feb 9, 2025
@openjdk
Copy link

openjdk bot commented Feb 9, 2025

@vnkozlov
The hotspot label was successfully removed.

Copy link
Contributor

@plummercj plummercj left a comment

Choose a reason for hiding this comment

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

I almost wished I hadn't looked because there is a lot of SA CodeBlob support that could use some cleanup. Most notably I think most of the wrapper subclasses are not needed by SA, and could be served by one common class. See what I'm doing in #23456 for JavaThread subclasses. Wrapper classes don't need to be 1-to-1 with the class type they are wrapping. A single wrapper class type can handle any number of hotspot types. It usually just make more sense for them to be 1-to-1, but when they are trivial and the implementation is replicated across subtypes, just having one wrapper class implement them all can simplify things.

The other thing I noticed is a lot of the subtypes seem to be doing some unnecessary things like registering Observers, and they all do something like the following:

44 Type type = db.lookupType("BufferBlob");

Even when it never references "type".

I'm not suggesting you clean up any of this now, but just pointed it out. I might file an issue and try to clean it up myself at some point.

I still need to take a closer look at the SA changes.


public class CodeCache {
private static GrowableArray<CodeHeap> heapArray;
private static VirtualConstructor virtualConstructor;
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason for switching from the virtualConstructor/hashMap approach to using getClassFor()? The hashmap is the model for JavaThread, MetaData, and CollectedHeap subtypes.

Copy link
Contributor

@plummercj plummercj Feb 10, 2025

Choose a reason for hiding this comment

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

I think I found the answer. Since there is no longer a vtable, TypeDataBase.addressTypeIsEqualToType() will no longer work for Codeblobs. I was wondering if the lack of a vtable might have some negative impact. Glad to see you found a solution. I hope the lack of a vtable does not creep in elsewhere. I know it will have some negative impact on things like the "findpc" functionality, which will no longer be able to tell the user that an address points to a Codeblob instance. There's no test for this, but users might run across it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is the reason for switching from the virtualConstructor/hashMap approach to using getClassFor()? The hashmap is the model for JavaThread, MetaData, and CollectedHeap subtypes.

I don't need any more mapping from CodeBlob class to corresponding virtual table name which does not exist anymore. CodeBlob::_kind field's value is used to determine which class should be used.

I think hashMap is overkill here. I can construct array Class<?> cbClasses[] and use cbClasses[CodeBlob::_kind] instead of if/else in getClassFor. But I would still need to check for unknown value of CodeBlob::_kind somehow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

impact on things like the "findpc" functionality

Do you mean findpc() function in VM which is used in debugger? Nothing should be changed for it.
It calls os::print_location() which calls CodeBlob::dump_for_addr(addr, st, verbose);:
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/os.cpp#L1278

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I was referring to the clhsdb findpc command, which uses PointerFinder, but actually that should be ok because it special cases the codecache and knows how to find CodeBlobs in it. It's the clhsdb "inspect" command that will no longer be able to identify the type for an address that points to the start of a CodeBlob. This is true of any address that points to the start of a hotspot C++ object that does not have a vtable, or is not declared in vmstructs. So it's not a new issue, but is just adding more types to the list that "inspect" won't figure out.

@dougxc
Copy link
Member

dougxc commented Feb 10, 2025

@dougxc and @tkrodriguez, please look if it affects Graal.

I'm pretty sure JVMCI does not care about the virtual-ness of these C++ classes. Running tier9 in mach5 is a good way to be sure.


#include <type_traits>

// Virtual methods are not allowed in code blobs to simplify caching compiled code.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it worth considering generating this code plus also some of the existing code in the header using an iterator template macro? e.g.

#define CODEBLOBS_DO(do_codeblob_abstract, do_codeblob_nonleaf, \
                     do_codeblob_leaf)                          \
  do_codeblob_abstract(CodeBlob)                                \
  do_codeblob_leaf(nmethod, Nmethod, nmethod)                   \
  do_codeblob_abstract(RuntimeBlob)                             \
  do_codeblob_nonleaf(BufferBlob, Buffer, buffer)               \
  do_codeblob_leaf(AdapterBlob, Adapter, adapter)               \
  . . .                                                         \
  do_codeblob_leaf(RuntimeStub, Runtime_Stub, runtime_stub)     \
  . . .

The macro arguments to the templates would themselves be macros:

do_codeblob_abstract(classname) // abstract, non-instantiable class
do_codeblob_nonleaf(classname, kindname, accessorname) // instantiable, subclassable
do_codeblob_leaf(classname, kindname, accessorname) // instantiable, non-subclassable

Using a template macro like this to generate the code below -- plus also some of the code currently declared piecemeal in the header -- would guarantee all cases are covered now and will remain so later so when the macro is updated. I think it would probably also allow case handling code in AOT cache code to be generated.

So, we would generate the code here as follows

#define EMPTY1(classname) 
#define EMPTY3(classname, kindname, accessorname)

#define assert_nonvirtual_leaf(classname, kindname, accessorname) \
  static_assert(!std::is_polymorphic<classname>::value,            \
                "no virtual methods are allowed in " # classname );

CODEBLOBS_DO(empty1, empty3, assert_nonvirtual_leaf)

#undef assert_nonvirtual_leaf

Likewise in codeBlob.hpp we could generate enum CodeBlobKind to cover all the non-abstract classes and likewise generate the accessor methods is_nmethod(), is_buffer_blob() in class CodeBlob which allow the kind to be tested.

#define codekind_enum_tag(classname, kindname, accessorname) \
  kindname,

enum CodeBlobKind : u1 {
  None,
  CODEBLOBS_DO(empty1, codekind_enum_tag, codekind_enum_tag)
  Number_Of_Kinds
};

#define is_codeblob_define(classname, kindname, accessorname) \
    void is_ # accessor_name () { return _kind == kindname; }

class CodeBlob {
  . . .
  CODEBLOBS_DO(empty1, is_codeblob_define, is_codeblob_define);
  . . .

There may be other opportunities to use the iterator (e.g. in vmStructs.cpp?) but this looks like a good start.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you @adinn for suggestion but no, I don't like macros - hard to debug and they add more complexity in this case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, understood!

Copy link
Member

@stefank stefank left a comment

Choose a reason for hiding this comment

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

We have a similar situation with oopDesc that are not allowed to have a vtable. The solution there is to use the Klass as the proxy vtable and then have a bunch of Klass::oop_ functions that act like virtual dispatch functions for associated oopDesc functions.

I wonder if a similar approach can be use here? Such an approach would (to me at lest) have the benefit that we don't have to spread switch statements in various functions in the top-most class.

If you are interested in seeing a prototype of this, take a look at this branch:
master...stefank:jdk:code_blob_vptr

Just a suggestion if you want to consider alternatives to these switch statements.

@vnkozlov
Copy link
Contributor Author

Thank you, @stefank. This is very interesting suggestion which I may take. I will check it.

@vnkozlov
Copy link
Contributor Author

Before I forgot to answer you, @plummercj
I completely agree with your comment about cleaning up wrapper subclasses which do nothing.

I think some wrapper subclasses for CodeBlob were kept because of is*() which were used only in PStack to print name. Why not use getName() for this purpose without big if/else there?

An other purpose could be a place holder for additional information in a future which never come.

Other wrapper provides information available in CodeBlob. Like RuntimeStub. callerMustGCArguments(). _caller_must_gc_arguments field is part of VM's CodeBlob class for some time now. Looks like I missed change in SA when did change in VM.

So yes, feel free to clean this up. I will help with review.

INTPTR_FORMAT " not found or invalid at %d",
p2i(_frame.pc()), decode_offset);
nm()->print_on(&ss);
nm()->print_on_v(&ss);
Copy link
Member

Choose a reason for hiding this comment

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

I suggest removing _v suffix to reduce changes and match existing naming.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Testing now.

Copy link
Member

@dean-long dean-long left a comment

Choose a reason for hiding this comment

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

HotSpot C++ changes look good. I skipped SA changes.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Feb 14, 2025
Copy link
Contributor Author

@vnkozlov vnkozlov left a comment

Choose a reason for hiding this comment

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

I addressed most @xmas92 and @dean-long comments and working on avoid _v suffix

Comment on lines 133 to 140
struct Vptr {
virtual void print_on(const CodeBlob* instance, outputStream* st) const {
instance->print_on_nv(st);
}
virtual void print_value_on(const CodeBlob* instance, outputStream* st) const {
instance->print_value_on_nv(st);
}
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


void print_on(outputStream* st) const override;
void print_value_on(outputStream* st) const override;
class Vptr : public CodeBlob::Vptr {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

void print_on(outputStream* st) const;
void print_value_on(outputStream* st) const;

class Vptr : public CodeBlob::Vptr {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed


void print_on(outputStream* st) const override;
void print_value_on(outputStream* st) const override;
class Vptr : public CodeBlob::Vptr {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed


void print_value_on(outputStream* st) const;

class Vptr : public CodeBlob::Vptr {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

void print_on(outputStream* st) const;
void print_value_on(outputStream* st) const;

class Vptr : public CodeBlob::Vptr {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@vnkozlov
Copy link
Contributor Author

Thank you, Dean, for review.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label Feb 15, 2025
@vnkozlov
Copy link
Contributor Author

I removed _v from CodeBlob::print*_on(st) methods to reduce scope of VM changes. But I have to add _impl suffix to these methods in CodeBlob subclasses.

I renamed nmethod::print_on(st, msg); to print_on_with_msg(at, msg) to avoid naming conflict C++ complains about. It cased change in dependencyContext.cpp`.

I made CodeBlob::Vptr class abstract as suggested.

I added empty Vptr class to RuntimeBlob because it is referenced in subclasses and corrected extensions in sublcasses to avoid mistakes @xmas92 pointed.

I also did some arguments renaming in SA in CodeCache.java as requested.

Tier1-5 testing passed.

Ready for new round of reviews.

Copy link
Member

@xmas92 xmas92 left a comment

Choose a reason for hiding this comment

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

Not looked at the SA changes.

lgtm.

Comment on lines +306 to +308

class Vptr : public CodeBlob::Vptr {
};
Copy link
Member

Choose a reason for hiding this comment

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

Was this needed for some compiler? Or is it to be more explicit about the type hierarchy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, @xmas92, for review and suggestions.

It is second (explicit type hierarchy). I think it should be explicitly declared (even empty) because it is referenced in subclasses to avoid confusion. And it could be useful in a future if we need other virtual methods.

Local build with gcc on Linux passed without it but I did not try to build on other platforms.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Feb 17, 2025
Copy link
Contributor

@plummercj plummercj left a comment

Choose a reason for hiding this comment

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

SA changes look good. Thanks for taking care of this.

@vnkozlov
Copy link
Contributor Author

Thank you, @plummercj , for review.

@vnkozlov
Copy link
Contributor Author

Thank you all for reviews and suggestions.

@vnkozlov
Copy link
Contributor Author

/integrate

@openjdk
Copy link

openjdk bot commented Feb 18, 2025

Going to push as commit 93b443ae36876bc182e06e142742945c65fe5349.
Since your change was applied there have been 60 commits pushed to the master branch:

  • e1d0a9c: 8350202: Tune for Power10 CPUs on Linux ppc64le
  • 885be2e: 8349908: RISC-V: C2 SelectFromTwoVector
  • 8193e0d: 8346280: C2: implement late barrier elision for G1
  • d7baae3: 8350178: Incorrect comment after JDK-8345580
  • 160db5f: 8340110: Ubsan: verifier.cpp:2043:19: runtime error: shift exponent 100 is too large for 32-bit type 'int'
  • ff05d97: 8349180: Remove redundant initialization in ciField constructor
  • 013fda1: 8348172: C2: Remove unused local variables in filter_helper() methods
  • 3353f8e: 8349652: Rewire nmethod oop load barriers
  • 8df8040: 8350093: RISC-V: java/math/BigInteger/LargeValueExceptions.java timeout with COH
  • 8ec5893: 8346781: [JVMCI] Limit ServiceLoader to class initializers
  • ... and 50 more: https://git.openjdk.org/jdk/compare/c5ac3c4f11e777b24d597deec522c9df09750f59...master

Your commit was automatically rebased without conflicts.

@openjdk
Copy link

openjdk bot commented Feb 18, 2025

@vnkozlov An unexpected error occurred during integration. No push attempt will be made. The error has been logged and will be investigated. It is possible that this error is caused by a transient issue; feel free to retry the operation.

@vnkozlov
Copy link
Contributor Author

/integrate

@openjdk
Copy link

openjdk bot commented Feb 18, 2025

Going to push as commit 46d4a60.
Since your change was applied there have been 61 commits pushed to the master branch:

  • 62d93f2: 8346050: Update BuildTestLib.gmk to build whole testlibrary
  • e1d0a9c: 8350202: Tune for Power10 CPUs on Linux ppc64le
  • 885be2e: 8349908: RISC-V: C2 SelectFromTwoVector
  • 8193e0d: 8346280: C2: implement late barrier elision for G1
  • d7baae3: 8350178: Incorrect comment after JDK-8345580
  • 160db5f: 8340110: Ubsan: verifier.cpp:2043:19: runtime error: shift exponent 100 is too large for 32-bit type 'int'
  • ff05d97: 8349180: Remove redundant initialization in ciField constructor
  • 013fda1: 8348172: C2: Remove unused local variables in filter_helper() methods
  • 3353f8e: 8349652: Rewire nmethod oop load barriers
  • 8df8040: 8350093: RISC-V: java/math/BigInteger/LargeValueExceptions.java timeout with COH
  • ... and 51 more: https://git.openjdk.org/jdk/compare/c5ac3c4f11e777b24d597deec522c9df09750f59...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Feb 18, 2025
@openjdk openjdk bot closed this Feb 18, 2025
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Feb 18, 2025
@openjdk
Copy link

openjdk bot commented Feb 18, 2025

@vnkozlov Pushed as commit 46d4a60.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

graal graal-dev@openjdk.org hotspot-compiler hotspot-compiler-dev@openjdk.org hotspot-runtime hotspot-runtime-dev@openjdk.org integrated Pull request has been integrated serviceability serviceability-dev@openjdk.org

Development

Successfully merging this pull request may close these issues.

8 participants