- 
                Notifications
    
You must be signed in to change notification settings  - Fork 6.1k
 
6312651: Compiler should only use verified interface types for optimization #10901
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
Conversation
| 
          
 👋 Welcome back roland! A progress list of the required criteria for merging this PR into   | 
    
          
Webrevs
  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, Roland! Overall, looks very good.
Submitted hs-tier1 - hs-tier4 testing. (Earlier, it went through hs-tier1 - hs-tier8 without new failures.)
Some minor comments/suggestions follow.
| GrowableArray<ciInstanceKlass*>* ciInstanceKlass::transitive_interfaces() const { | ||
| GrowableArray<ciInstanceKlass*>* result = NULL; | ||
| GUARDED_VM_ENTRY( | ||
| InstanceKlass* ik = get_instanceKlass(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to cache the result on ciInstanceKlass instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe but it's not as simple as it seems (for the reason discussed below for vm classes). Some classes are allocated in a special arena because they are shared between compilations. A _transitive_interfaces array would need to be allocated in that same arena (otherwise if lazily allocated during a particular compilation it would be in the thread's resource are and its content would be wiped out when the compilation is over but still reachable). That's not straightforward with the current implementation as the arena a ciInstanceKlass is allocated in is not available to the ciInstanceKlass so that would require extra changes and I'm not sure the extra complexity is worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scratch that. I see it's done for _nonstatic_fields and will do the same.
| if (vmClasses::name##_is_loaded()) { \ | ||
| InstanceKlass* ik = vmClasses::name(); \ | ||
| ciEnv::_##name = get_metadata(ik)->as_instance_klass(); \ | ||
| Array<InstanceKlass*>* interfaces = ik->transitive_interfaces(); \ | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of interface-related part of the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ciInstanceKlass objects for the vm classes all need to be allocated from the same long lived arena that's created in ciObjectFactory::initialize() because they are shared between compilations. Without that code, the ciInstanceKlass for a particular vm class is in the long lived arena but the ciInstanceKlass objects for the interfaces are created later when they are needed in the arena of some compilation. Once that compilation is over the interface objects are destroyed but still referenced from shared types such as TypeInstPtr::MIRROR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, got it now. Is it really needed considering there's transitive_interfaces() call later?
        
          
                src/hotspot/share/opto/type.hpp
              
                Outdated
          
        
      | // If the klass is final, the resulting type will be exact. | ||
| static const TypeOopPtr* make_from_klass(ciKlass* klass) { | ||
| return make_from_klass_common(klass, true, false); | ||
| static const TypeOopPtr* make_from_klass(ciKlass* klass, bool trust_interface = false) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest to use an enum (trust_interfaces/ignore_interfaces) instead of a bool, so the intention is clear at call sites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion. I will make that change.
        
          
                src/hotspot/share/opto/type.cpp
              
                Outdated
          
        
      | mreg2type[Op_RegFlags] = TypeInt::CC; | ||
| 
               | 
          ||
| TypeAryPtr::_array_interfaces = new TypePtr::InterfaceSet(); | ||
| GrowableArray<ciInstanceKlass*>* array_interfaces = ciArrayKlass::interfaces(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe move the code into a constructor or a factory method?
After that, the only user of TypePtr::InterfaceSet::add() will be TypePtr::interfaces().
It would be nice to make TypePtr::InterfaceSet immutable and cache query results (InterfaceSet::is_loaded()  and InterfaceSet::exact_klass()).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion as well.
        
          
                src/hotspot/share/opto/type.cpp
              
                Outdated
          
        
      | } | ||
| return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative, depth); | ||
| interfaces = this_interfaces.intersection_with(tp_interfaces); | ||
| return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, NULL,offset, instance_id, speculative, depth); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NULL,offset
missing space
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.
        
          
                src/hotspot/share/opto/type.cpp
              
                Outdated
          
        
      | // below the centerline when the superclass is exact. We need to | ||
| // do the same here. | ||
| if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) { | ||
| if (klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces.intersection_with(tp_interfaces).eq(this_interfaces) && !klass_is_exact()) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this_interfaces.intersection_with(tp_interfaces).eq(this_interfaces)
Maybe a case for a helper method InterfaceSet::contains(InterfaceSet)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, there are several uses of this pattern. I will make that change.
        
          
                src/hotspot/share/opto/type.cpp
              
                Outdated
          
        
      | ciInstanceKlass* ik = k->as_instance_klass(); | ||
| bool klass_is_exact = ik->is_final(); | ||
| if (!klass_is_exact && | ||
| deps != NULL && UseUniqueSubclasses) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, put UseUniqueSubclasses guard at the top of the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.
| 
           BTW will there still be a need in   | 
    
          
 In principle, probably not.  | 
    
          
 Thanks for reviewing this. I pushed new commits that should address your comments/suggestions.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better, thanks!
Minor comments/suggestions follow.
FTR test results are clean. I'll submit performance testing shortly.
| if (vmClasses::name##_is_loaded()) { \ | ||
| InstanceKlass* ik = vmClasses::name(); \ | ||
| ciEnv::_##name = get_metadata(ik)->as_instance_klass(); \ | ||
| Array<InstanceKlass*>* interfaces = ik->transitive_interfaces(); \ | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, got it now. Is it really needed considering there's transitive_interfaces() call later?
| } | ||
| } | ||
| 
               | 
          ||
| GrowableArray<ciInstanceKlass*>* ciArrayKlass::interfaces() { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR there's a subtle asymmetry between ciArrayKlass::interfaces() and ciInstanceKlass::transitive_interfaces() when it comes to memory allocation: the former allocates from resource area while the latter from compiler arena.
It doesn't cause any problems since ciArrayKlass::interfaces() is used only in Type::Initialize_shared() to instantiate shared TypeAryPtr::_array_interfaces, but it took me some time to find that out.
Maybe a helper method in type.cpp is a better place for that logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that work? ciArrayKlass::interfaces() has to transition into the vm which is not something that would feel right in type.cpp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, good point.
IMO you could just work directly with CI mirrors for Serializable and Cloneable, but now I'm curious why does CI code diverge from ObjArrayKlass::compute_secondary_supers()?
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/oops/objArrayKlass.cpp#L376
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And to answer my question: only Serializable and Cloneable are interfaces among those. The rest are arrays.
| GrowableArray<ciInstanceKlass*>* ciInstanceKlass::transitive_interfaces() { | ||
| if (_transitive_interfaces == NULL) { | ||
| GUARDED_VM_ENTRY( | ||
| InstanceKlass* ik = get_instanceKlass(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A candidate for compute_transitive_interfaces() helper method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in new commit.
        
          
                src/hotspot/share/opto/subnode.cpp
              
                Outdated
          
        
      | // return the ConP(Foo.klass) | ||
| assert(mirror_type->is_klass(), "mirror_type should represent a Klass*"); | ||
| return phase->makecon(TypeKlassPtr::make(mirror_type->as_klass())); | ||
| return phase->makecon(TypeKlassPtr::make(mirror_type->as_klass(), Type::trust_interfaces)); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
        
          
                src/hotspot/share/opto/type.cpp
              
                Outdated
          
        
      | // | ||
| assert(loaded->ptr() != TypePtr::Null, "insanity check"); | ||
| // | ||
| if( loaded->ptr() == TypePtr::TopPTR ) { return unloaded; } | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after if.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
        
          
                src/hotspot/share/opto/type.cpp
              
                Outdated
          
        
      | 
               | 
          ||
| // Both are unloaded, not the same class, not Object | ||
| // Or meet unloaded with a different loaded class, not java/lang/Object | ||
| if( ptr != TypePtr::BotPTR ) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after if.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
          
 Thanks. I updated the patch and removed the interface specific code in the VM_CLASS_DEFN() macro that's indeed no longer needed.  | 
    
| 
           FTR performance results look good.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, Roland! I'm approving the PR. (hs-tier1 - hs-tier2 sanity testing passed with latest version.)
Feel free to handle ciArrayKlass::interfaces() as you find most appropriate.
| 
           @rwestrel 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: 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 167 new commits pushed to the  
 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   | 
    
          
 Thanks for the review (and running tests).  | 
    
          
 Yes, it is much clearer now. Thanks.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work. Thanks!
| 
           /integrate  | 
    
| 
          
 Going to push as commit 45d1807. 
 Your commit was automatically rebased without conflicts.  | 
    
This change is mostly the same I sent for review 3 years ago but was
never integrated:
https://mail.openjdk.org/pipermail/hotspot-compiler-dev/2019-May/033803.html
The main difference is that, in the meantime, I submitted a couple of
refactoring changes extracted from the 2019 patch:
8266550: C2: mirror TypeOopPtr/TypeInstPtr/TypeAryPtr with TypeKlassPtr/TypeInstKlassPtr/TypeAryKlassPtr
8275201: C2: hide klass() accessor from TypeOopPtr and typeKlassPtr subclasses
As a result, the current patch is much smaller (but still not small).
The implementation is otherwise largely the same as in the 2019
patch. I tried to remove some of the code duplication between the
TypeOopPtr and TypeKlassPtr hierarchies by having some of the logic
shared in template methods. In the 2019 patch, interfaces were trusted
when types were constructed and I had added code to drop interfaces
from a type where they couldn't be trusted. This new patch proceeds
the other way around: interfaces are not trusted when a type is
constructed and code that uses the type must explicitly request that
they are included (this was suggested as an improvement by Vladimir
Ivanov I think).
Progress
Issue
Reviewers
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/10901/head:pull/10901$ git checkout pull/10901Update a local copy of the PR:
$ git checkout pull/10901$ git pull https://git.openjdk.org/jdk pull/10901/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 10901View PR using the GUI difftool:
$ git pr show -t 10901Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/10901.diff