Skip to content

Commit 0ce8efd

Browse files
committed
[GR-34484] [GR-33888] Refine the class hierarchy assumption in ObjectKlass to keep track of classes which have no strict concrete subclasses
PullRequest: graal/10161
2 parents 9b18a54 + 77161d5 commit 0ce8efd

File tree

9 files changed

+172
-93
lines changed

9 files changed

+172
-93
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@
2929
* A wrapper around {@link Assumption}. Ensures that class hierarchy assumptions are managed
3030
* exclusively by {@link ClassHierarchyOracle}.
3131
*/
32-
public interface LeafTypeAssumption {
32+
public interface ClassHierarchyAssumption {
3333
Assumption getAssumption();
3434
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package com.oracle.truffle.espresso.analysis.hierarchy;
25+
26+
import com.oracle.truffle.api.Assumption;
27+
import com.oracle.truffle.api.Truffle;
28+
import com.oracle.truffle.api.utilities.AlwaysValidAssumption;
29+
import com.oracle.truffle.api.utilities.NeverValidAssumption;
30+
import com.oracle.truffle.espresso.impl.ObjectKlass;
31+
32+
final class ClassHierarchyAssumptionImpl implements ClassHierarchyAssumption {
33+
static final ClassHierarchyAssumption AlwaysValid = new ClassHierarchyAssumptionImpl(AlwaysValidAssumption.INSTANCE);
34+
static final ClassHierarchyAssumption NeverValid = new ClassHierarchyAssumptionImpl(NeverValidAssumption.INSTANCE);
35+
36+
private final Assumption underlying;
37+
38+
// Used only to create never valid and always valid instances
39+
private ClassHierarchyAssumptionImpl(Assumption underlyingAssumption) {
40+
underlying = underlyingAssumption;
41+
}
42+
43+
private ClassHierarchyAssumptionImpl(String assumptionName) {
44+
underlying = Truffle.getRuntime().createAssumption(assumptionName);
45+
}
46+
47+
ClassHierarchyAssumptionImpl(ObjectKlass klass) {
48+
this(klass.getNameAsString() + " has no concrete subclasses");
49+
}
50+
51+
@Override
52+
public Assumption getAssumption() {
53+
return underlying;
54+
}
55+
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/analysis/hierarchy/ClassHierarchyOracle.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,43 @@
2929
/**
3030
* {@code ClassHierarchyOracle} provides information about class hierarchy in the guest code,
3131
* evolving as new classes are loaded. The oracle is safe, i.e. its answers never cause incorrect
32-
* optimizations, but not necessarily precise (e.g. may not detect an effectively leaf type).
32+
* optimizations, but not necessarily precise (e.g. may not detect that a class has no subclasses).
3333
* <p>
3434
* The oracle is only valid within an {@link EspressoContext}.
3535
*/
3636
public interface ClassHierarchyOracle {
3737
final class ClassHierarchyAccessor {
38-
protected ClassHierarchyAccessor() {
38+
static final ClassHierarchyAccessor accessor = new ClassHierarchyAccessor();
39+
40+
private ClassHierarchyAccessor() {
3941
}
4042
}
4143

4244
/**
43-
* Must be called to initialize {@code leafTypeAssumption} of {@code newKlass}. In addition, it
44-
* communicates to the oracle that a new klass has been created and its ancestors are no longer
45-
* leaves.
45+
* Must be called to initialize {@code noConcreteSubclassesAssumption} of {@code newKlass}. In
46+
* addition, it communicates to the oracle that a new klass has been created and its ancestors
47+
* are no longer leaves.
4648
*
4749
* @param newKlass -- newly created class
48-
* @return the assumption, indicating whether the class is a leaf in class hierarchy.
50+
* @return the assumption, indicating whether the class has subclasses
51+
*/
52+
ClassHierarchyAssumption createAssumptionForNewKlass(ObjectKlass newKlass);
53+
54+
/**
55+
* @return the assumption, valid only if {@code klass} is a concrete class and has no concrete
56+
* subclasses. Automatically invalidated in
57+
* {@link #createAssumptionForNewKlass(ObjectKlass)} when a concrete child of
58+
* {@code klass} is created.
4959
*/
50-
LeafTypeAssumption createAssumptionForNewKlass(ObjectKlass newKlass);
60+
ClassHierarchyAssumption isLeaf(ObjectKlass klass);
5161

5262
/**
53-
* @return the assumption, valid iff {@code klass} is a leaf in class hierarchy. Automatically
54-
* invalidated in {@link #createAssumptionForNewKlass(ObjectKlass)} when a child of
63+
* @return the assumption, valid only if {@code klass} has no implementors, including itself
64+
* (i.e. it must be abstract or an interface). Automatically invalidated in
65+
* {@link #createAssumptionForNewKlass(ObjectKlass)} when a concrete child of
5566
* {@code klass} is created.
5667
*/
57-
LeafTypeAssumption isLeafClass(ObjectKlass klass);
68+
ClassHierarchyAssumption hasNoImplementors(ObjectKlass klass);
5869

5970
SingleImplementor initializeImplementorForNewKlass(ObjectKlass klass);
6071

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/analysis/hierarchy/DefaultClassHierarchyOracle.java

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,43 +28,61 @@
2828

2929
/**
3030
* Computes the classes that are effectively final by keeping track of currently loaded classes. To
31-
* compute currently leaf classes, it creates {@code leafTypeAssumption} in the {@link ObjectKlass}
32-
* constructor and invalidates it when a descendant of this class is initialized.
31+
* compute currently leaf classes, it creates {@code noConcreteSubclassesAssumption} in the
32+
* {@link ObjectKlass} constructor and invalidates it when a descendant of this class is
33+
* initialized.
3334
*/
34-
public class DefaultClassHierarchyOracle extends NoOpClassHierarchyOracle implements ClassHierarchyOracle {
35+
public class DefaultClassHierarchyOracle implements ClassHierarchyOracle {
3536
@Override
36-
public LeafTypeAssumption createAssumptionForNewKlass(ObjectKlass newKlass) {
37-
if (newKlass.isAbstract() || newKlass.isInterface()) {
38-
return NotLeaf;
37+
public ClassHierarchyAssumption createAssumptionForNewKlass(ObjectKlass newKlass) {
38+
if (newKlass.isConcrete()) {
39+
addImplementorToAncestors(newKlass);
3940
}
40-
// this is a concrete klass, add it to implementors of super classes and interfaces
41-
addImplementorToSuperInterfaces(newKlass);
4241
if (newKlass.isFinalFlagSet()) {
43-
return FinalIsAlwaysLeaf;
42+
return ClassHierarchyAssumptionImpl.AlwaysValid;
43+
}
44+
return new ClassHierarchyAssumptionImpl(newKlass);
45+
}
46+
47+
@Override
48+
public ClassHierarchyAssumption isLeaf(ObjectKlass klass) {
49+
if (klass.isConcrete()) {
50+
return klass.getNoConcreteSubclassesAssumption(ClassHierarchyAccessor.accessor);
51+
} else {
52+
return ClassHierarchyAssumptionImpl.NeverValid;
53+
}
54+
}
55+
56+
@Override
57+
public ClassHierarchyAssumption hasNoImplementors(ObjectKlass klass) {
58+
if (klass.isAbstract() || klass.isInterface()) {
59+
return klass.getNoConcreteSubclassesAssumption(ClassHierarchyAccessor.accessor);
60+
} else {
61+
return ClassHierarchyAssumptionImpl.NeverValid;
4462
}
45-
return new LeafTypeAssumptionImpl(newKlass);
4663
}
4764

4865
/**
4966
* Recursively adds {@code implementor} as an implementor of {@code superInterface} and its
5067
* parent interfaces.
5168
*/
5269
private void addImplementor(ObjectKlass superInterface, ObjectKlass implementor) {
53-
superInterface.getImplementor(classHierarchyInfoAccessor).addImplementor(implementor);
70+
superInterface.getNoConcreteSubclassesAssumption(ClassHierarchyAccessor.accessor).getAssumption().invalidate();
71+
superInterface.getImplementor(ClassHierarchyAccessor.accessor).addImplementor(implementor);
5472
for (ObjectKlass ancestorInterface : superInterface.getSuperInterfaces()) {
5573
addImplementor(ancestorInterface, implementor);
5674
}
5775
}
5876

59-
private void addImplementorToSuperInterfaces(ObjectKlass newKlass) {
77+
private void addImplementorToAncestors(ObjectKlass newKlass) {
6078
for (ObjectKlass superInterface : newKlass.getSuperInterfaces()) {
6179
addImplementor(superInterface, newKlass);
6280
}
6381

6482
ObjectKlass currentKlass = newKlass.getSuperKlass();
6583
while (currentKlass != null) {
66-
currentKlass.getLeafTypeAssumption(classHierarchyInfoAccessor).getAssumption().invalidate();
67-
currentKlass.getImplementor(classHierarchyInfoAccessor).addImplementor(newKlass);
84+
currentKlass.getNoConcreteSubclassesAssumption(ClassHierarchyAccessor.accessor).getAssumption().invalidate();
85+
currentKlass.getImplementor(ClassHierarchyAccessor.accessor).addImplementor(newKlass);
6886
for (ObjectKlass superInterface : currentKlass.getSuperInterfaces()) {
6987
addImplementor(superInterface, newKlass);
7088
}
@@ -86,6 +104,6 @@ public SingleImplementor initializeImplementorForNewKlass(ObjectKlass klass) {
86104

87105
@Override
88106
public AssumptionGuardedValue<ObjectKlass> readSingleImplementor(ObjectKlass klass) {
89-
return klass.getImplementor(classHierarchyInfoAccessor).read();
107+
return klass.getImplementor(ClassHierarchyAccessor.accessor).read();
90108
}
91109
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/analysis/hierarchy/NoOpClassHierarchyOracle.java

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,57 +23,34 @@
2323

2424
package com.oracle.truffle.espresso.analysis.hierarchy;
2525

26-
import com.oracle.truffle.api.Assumption;
27-
import com.oracle.truffle.api.Truffle;
28-
import com.oracle.truffle.api.utilities.AlwaysValidAssumption;
29-
import com.oracle.truffle.api.utilities.NeverValidAssumption;
3026
import com.oracle.truffle.espresso.impl.ObjectKlass;
3127

3228
/**
3329
* An implementation of {@link ClassHierarchyOracle} which simply checks {@code final} modifier of a
3430
* class.
3531
*/
3632
public class NoOpClassHierarchyOracle implements ClassHierarchyOracle {
37-
protected static class LeafTypeAssumptionImpl implements LeafTypeAssumption {
38-
private final Assumption underlying;
39-
40-
// Used only to create never valid and always valid instances
41-
private LeafTypeAssumptionImpl(Assumption underlyingAssumption) {
42-
underlying = underlyingAssumption;
43-
}
44-
45-
private LeafTypeAssumptionImpl(String assumptionName) {
46-
underlying = Truffle.getRuntime().createAssumption(assumptionName);
47-
}
48-
49-
LeafTypeAssumptionImpl(ObjectKlass klass) {
50-
this(klass.getNameAsString() + " is a leaf type");
51-
}
33+
protected static final AssumptionGuardedValue<ObjectKlass> NotSingleImplementor = AssumptionGuardedValue.createInvalid();
5234

53-
@Override
54-
public Assumption getAssumption() {
55-
return underlying;
35+
@Override
36+
public ClassHierarchyAssumption createAssumptionForNewKlass(ObjectKlass newKlass) {
37+
if (newKlass.isFinalFlagSet()) {
38+
return ClassHierarchyAssumptionImpl.AlwaysValid;
5639
}
40+
return ClassHierarchyAssumptionImpl.NeverValid;
5741
}
5842

59-
protected static final ClassHierarchyAccessor classHierarchyInfoAccessor = new ClassHierarchyAccessor();
60-
61-
protected static final LeafTypeAssumption FinalIsAlwaysLeaf = new LeafTypeAssumptionImpl(AlwaysValidAssumption.INSTANCE);
62-
protected static final LeafTypeAssumption NotLeaf = new LeafTypeAssumptionImpl(NeverValidAssumption.INSTANCE);
63-
64-
protected static final AssumptionGuardedValue<ObjectKlass> NotSingleImplementor = AssumptionGuardedValue.createInvalid();
65-
6643
@Override
67-
public LeafTypeAssumption createAssumptionForNewKlass(ObjectKlass newKlass) {
68-
if (newKlass.isFinalFlagSet()) {
69-
return FinalIsAlwaysLeaf;
44+
public ClassHierarchyAssumption isLeaf(ObjectKlass klass) {
45+
if (klass.isFinalFlagSet()) {
46+
return ClassHierarchyAssumptionImpl.AlwaysValid;
7047
}
71-
return NotLeaf;
48+
return ClassHierarchyAssumptionImpl.NeverValid;
7249
}
7350

7451
@Override
75-
public LeafTypeAssumption isLeafClass(ObjectKlass klass) {
76-
return klass.getLeafTypeAssumption(classHierarchyInfoAccessor);
52+
public ClassHierarchyAssumption hasNoImplementors(ObjectKlass klass) {
53+
return ClassHierarchyAssumptionImpl.NeverValid;
7754
}
7855

7956
@Override

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/ObjectKlass.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
import com.oracle.truffle.api.Truffle;
4949
import com.oracle.truffle.api.nodes.ExplodeLoop;
5050
import com.oracle.truffle.espresso.analysis.hierarchy.ClassHierarchyOracle.ClassHierarchyAccessor;
51-
import com.oracle.truffle.espresso.analysis.hierarchy.LeafTypeAssumption;
51+
import com.oracle.truffle.espresso.analysis.hierarchy.ClassHierarchyAssumption;
5252
import com.oracle.truffle.espresso.analysis.hierarchy.ClassHierarchyOracle;
5353
import com.oracle.truffle.espresso.analysis.hierarchy.SingleImplementor;
5454
import com.oracle.truffle.espresso.classfile.ConstantPool;
@@ -149,7 +149,7 @@ public final class ObjectKlass extends Klass {
149149
// Class hierarchy information is managed by ClassHierarchyOracle, stored in ObjectKlass only
150150
// for convenience.
151151
// region class hierarchy information
152-
private final LeafTypeAssumption leafTypeAssumption;
152+
private final ClassHierarchyAssumption noConcreteSubclassesAssumption;
153153
private final SingleImplementor implementor;
154154
// endregion
155155

@@ -241,7 +241,7 @@ public ObjectKlass(EspressoContext context, LinkedKlass linkedKlass, ObjectKlass
241241
initSelfReferenceInPool();
242242
}
243243

244-
this.leafTypeAssumption = getContext().getClassHierarchyOracle().createAssumptionForNewKlass(this);
244+
this.noConcreteSubclassesAssumption = getContext().getClassHierarchyOracle().createAssumptionForNewKlass(this);
245245
this.implementor = getContext().getClassHierarchyOracle().initializeImplementorForNewKlass(this);
246246
this.initState = LOADED;
247247
assert verifyTables();
@@ -1418,12 +1418,12 @@ public void removeByRedefinition() {
14181418
* {@code assumptionAccessor}. The assumption is stored in ObjectKlass for easy mapping between
14191419
* classes and corresponding assumptions.
14201420
*
1421-
* @return the assumption, indicating if this class is a leaf in class hierarchy.
1422-
* @see ClassHierarchyOracle#isLeafClass(ObjectKlass)
1421+
* @see ClassHierarchyOracle#isLeaf(ObjectKlass)
1422+
* @see ClassHierarchyOracle#hasNoImplementors(ObjectKlass)
14231423
*/
1424-
public LeafTypeAssumption getLeafTypeAssumption(ClassHierarchyAccessor assumptionAccessor) {
1424+
public ClassHierarchyAssumption getNoConcreteSubclassesAssumption(ClassHierarchyAccessor assumptionAccessor) {
14251425
Objects.requireNonNull(assumptionAccessor);
1426-
return leafTypeAssumption;
1426+
return noConcreteSubclassesAssumption;
14271427
}
14281428

14291429
public SingleImplementor getImplementor(ClassHierarchyAccessor accessor) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/bytecodes/InstanceOf.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import com.oracle.truffle.api.nodes.NodeInfo;
3232
import com.oracle.truffle.api.profiles.BranchProfile;
3333
import com.oracle.truffle.espresso.analysis.hierarchy.AssumptionGuardedValue;
34-
import com.oracle.truffle.espresso.analysis.hierarchy.LeafTypeAssumption;
34+
import com.oracle.truffle.espresso.analysis.hierarchy.ClassHierarchyAssumption;
3535
import com.oracle.truffle.espresso.impl.ArrayKlass;
3636
import com.oracle.truffle.espresso.impl.Klass;
3737
import com.oracle.truffle.espresso.impl.ObjectKlass;
@@ -229,22 +229,18 @@ abstract static class ConstantClass extends InstanceOf {
229229
this.superType = superType;
230230
}
231231

232-
protected LeafTypeAssumption getLeafAssumption() {
233-
return EspressoContext.get(this).getClassHierarchyOracle().isLeafClass(superType);
232+
protected ClassHierarchyAssumption getNoImplementorsAssumption() {
233+
return EspressoContext.get(this).getClassHierarchyOracle().hasNoImplementors(superType);
234234
}
235235

236236
protected AssumptionGuardedValue<ObjectKlass> readSingleImplementor() {
237237
return EspressoContext.get(this).getClassHierarchyOracle().readSingleImplementor(superType);
238238
}
239239

240-
/**
241-
* If {@code superType} is a leaf type, {@code maybeSubtype} is a subtype of
242-
* {@code superType} iff it is equal to {@code superType}.
243-
*/
244-
@Specialization(assumptions = "superTypeIsLeaf")
245-
public boolean doLeaf(ObjectKlass maybeSubtype,
246-
@SuppressWarnings("unused") @Cached("getLeafAssumption().getAssumption()") Assumption superTypeIsLeaf) {
247-
return superType == maybeSubtype;
240+
@Specialization(assumptions = "noImplementors")
241+
public boolean doNoImplementors(@SuppressWarnings("unused") Klass maybeSubtype,
242+
@SuppressWarnings("unused") @Cached("getNoImplementorsAssumption().getAssumption()") Assumption noImplementors) {
243+
return false;
248244
}
249245

250246
/**
@@ -259,7 +255,7 @@ public boolean doSingleImplementor(ObjectKlass maybeSubtype,
259255
return maybeSubtype == implementor;
260256
}
261257

262-
@Specialization(replaces = {"doLeaf", "doSingleImplementor"})
258+
@Specialization
263259
public boolean doObjectKlass(ObjectKlass maybeSubtype) {
264260
return superType == maybeSubtype || superType.checkOrdinaryClassSubclassing(maybeSubtype);
265261
}
@@ -280,11 +276,21 @@ abstract static class ConstantInterface extends InstanceOf {
280276
assert superType.isInterface();
281277
}
282278

279+
protected ClassHierarchyAssumption getNoImplementorsAssumption() {
280+
return EspressoContext.get(this).getClassHierarchyOracle().hasNoImplementors(superType);
281+
}
282+
283283
protected AssumptionGuardedValue<ObjectKlass> readSingleImplementor() {
284284
return EspressoContext.get(this).getClassHierarchyOracle().readSingleImplementor(superType);
285285
}
286286

287-
@Specialization(assumptions = "maybeSingleImplementor.hasValue()", guards = "implementor != null")
287+
@Specialization(assumptions = "noImplementors")
288+
public boolean doNoImplementors(@SuppressWarnings("unused") Klass maybeSubtype,
289+
@SuppressWarnings("unused") @Cached("getNoImplementorsAssumption().getAssumption()") Assumption noImplementors) {
290+
return false;
291+
}
292+
293+
@Specialization(assumptions = "maybeSingleImplementor.hasValue()", guards = "implementor != null", replaces = "doNoImplementors")
288294
public boolean doSingleImplementor(ObjectKlass maybeSubtype,
289295
@SuppressWarnings("unused") @Cached("readSingleImplementor()") AssumptionGuardedValue<ObjectKlass> maybeSingleImplementor,
290296
@Cached("maybeSingleImplementor.get()") ObjectKlass implementor) {

0 commit comments

Comments
 (0)