Skip to content

Commit d2b8213

Browse files
committed
Refactor IsInstanceNode and IsSubClassNode to common base and avoid using IndirectCall
1 parent 6fd5414 commit d2b8213

File tree

3 files changed

+116
-133
lines changed

3 files changed

+116
-133
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 96 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import java.math.BigInteger;
7878
import java.nio.charset.Charset;
7979
import java.nio.charset.CodingErrorAction;
80+
import java.util.ArrayDeque;
8081
import java.util.ArrayList;
8182
import java.util.Arrays;
8283
import java.util.List;
@@ -178,7 +179,6 @@
178179
import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode;
179180
import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode;
180181
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
181-
import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext;
182182
import com.oracle.graal.python.runtime.PythonContext;
183183
import com.oracle.graal.python.runtime.PythonCore;
184184
import com.oracle.graal.python.runtime.PythonOptions;
@@ -1037,108 +1037,88 @@ Object doObject(Object value,
10371037
}
10381038
}
10391039

1040-
// isinstance(object, classinfo)
1041-
@Builtin(name = ISINSTANCE, minNumOfPositionalArgs = 2)
1042-
@GenerateNodeFactory
1043-
public abstract static class IsInstanceNode extends PythonBinaryBuiltinNode {
1040+
/**
1041+
* Base class for {@code isinstance} and {@code issubclass} that implements the recursive
1042+
* iteration of tuples passed as the second argument. The inheriting classes need to just
1043+
* provide the base for the recursion.
1044+
*/
1045+
public abstract static class RecursiveBinaryCheckBaseNode extends PythonBinaryBuiltinNode {
10441046
static final int MAX_EXPLODE_LOOP = 16; // is also divided by recursion depth+1
1047+
static final byte STOP_RECURSION = Byte.MAX_VALUE;
10451048

10461049
@Child private SequenceStorageNodes.LenNode lenNode;
10471050
@Child private GetObjectArrayNode getObjectArrayNode;
10481051
protected final byte depth;
10491052

1050-
protected IsInstanceNode(byte depth) {
1053+
protected RecursiveBinaryCheckBaseNode(byte depth) {
10511054
this.depth = depth;
10521055
}
10531056

1054-
protected IsInstanceNode() {
1055-
this((byte) 0);
1056-
}
1057+
public abstract boolean executeWith(VirtualFrame frame, Object instance, Object cls);
10571058

1058-
public static IsInstanceNode createRecursive(byte currentDepth) {
1059-
return BuiltinFunctionsFactory.IsInstanceNodeFactory.create((byte) (currentDepth + 1));
1059+
public final RecursiveBinaryCheckBaseNode createRecursive() {
1060+
return createRecursive((byte) (depth + 1));
10601061
}
10611062

1062-
private static TriState isInstanceCheckInternal(VirtualFrame frame, Object instance, Object cls, LookupAndCallBinaryNode instanceCheckNode, CoerceToBooleanNode castToBooleanNode) {
1063-
Object instanceCheckResult = instanceCheckNode.executeObject(frame, cls, instance);
1064-
if (instanceCheckResult == NOT_IMPLEMENTED) {
1065-
return TriState.UNDEFINED;
1066-
}
1067-
return TriState.valueOf(castToBooleanNode.executeBoolean(frame, instanceCheckResult));
1068-
}
1069-
1070-
public abstract boolean executeWith(VirtualFrame frame, Object instance, Object cls);
1071-
1072-
@Specialization(guards = "isPythonClass(cls)")
1073-
static boolean isInstance(VirtualFrame frame, Object instance, Object cls,
1074-
@Shared("instanceCheck") @Cached("create(__INSTANCECHECK__)") LookupAndCallBinaryNode instanceCheckNode,
1075-
@Shared("boolCast") @Cached("createIfTrueNode()") CoerceToBooleanNode castToBooleanNode,
1076-
@Cached GetClassNode getClassNode,
1077-
@Cached TypeNodes.IsSameTypeNode isSameTypeNode,
1078-
@Cached IsSubtypeNode isSubtypeNode) {
1079-
Object instanceClass = getClassNode.execute(instance);
1080-
return isSameTypeNode.execute(instanceClass, cls) || isSubtypeNode.execute(frame, instanceClass, cls)//
1081-
|| isInstanceCheckInternal(frame, instance, cls, instanceCheckNode, castToBooleanNode) == TriState.TRUE;
1063+
protected RecursiveBinaryCheckBaseNode createRecursive(@SuppressWarnings("unused") byte newDepth) {
1064+
throw new AbstractMethodError(); // Cannot be really abstract b/c Truffle DSL...
10821065
}
10831066

10841067
protected int getMaxExplodeLoop() {
10851068
return MAX_EXPLODE_LOOP / (depth + 1);
10861069
}
10871070

1088-
@Specialization(guards = {"depth < getNodeRecursionLimit()", "getLength(clsTuple) == cachedLen", "cachedLen < getMaxExplodeLoop()"}, limit = "getVariableArgumentInlineCacheLimit()")
1071+
@Specialization(guards = {"depth != STOP_RECURSION", "depth < getNodeRecursionLimit()", //
1072+
"getLength(clsTuple) == cachedLen", "cachedLen < getMaxExplodeLoop()"}, //
1073+
limit = "getVariableArgumentInlineCacheLimit()")
10891074
@ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN)
1090-
boolean isInstanceTupleConstantLen(VirtualFrame frame, Object instance, PTuple clsTuple,
1075+
final boolean doTupleConstantLen(VirtualFrame frame, Object instance, PTuple clsTuple,
10911076
@Cached("getLength(clsTuple)") int cachedLen,
1092-
@Cached("createRecursive(depth)") IsInstanceNode isInstanceNode) {
1077+
@Cached("createRecursive(depth)") RecursiveBinaryCheckBaseNode recursiveNode) {
10931078
Object[] array = getArray(clsTuple);
10941079
for (int i = 0; i < cachedLen; i++) {
10951080
Object cls = array[i];
1096-
if (isInstanceNode.executeWith(frame, instance, cls)) {
1081+
if (recursiveNode.executeWith(frame, instance, cls)) {
10971082
return true;
10981083
}
10991084
}
11001085
return false;
11011086
}
11021087

1103-
@Specialization(guards = "depth < getNodeRecursionLimit()", replaces = "isInstanceTupleConstantLen")
1104-
boolean isInstanceRecursiveNode(VirtualFrame frame, Object instance, PTuple clsTuple,
1105-
@Cached("createRecursive(depth)") IsInstanceNode instanceNode) {
1088+
@Specialization(guards = {"depth != STOP_RECURSION", "depth < getNodeRecursionLimit()"}, replaces = "doTupleConstantLen")
1089+
final boolean doRecursiveWithNode(VirtualFrame frame, Object instance, PTuple clsTuple,
1090+
@Cached("createRecursive(depth)") RecursiveBinaryCheckBaseNode recursiveNode) {
11061091
for (Object cls : getArray(clsTuple)) {
1107-
if (instanceNode.executeWith(frame, instance, cls)) {
1092+
if (recursiveNode.executeWith(frame, instance, cls)) {
11081093
return true;
11091094
}
11101095
}
11111096
return false;
11121097
}
11131098

1114-
@Specialization(guards = "depth >= getNodeRecursionLimit()")
1115-
boolean isInstanceRecursiveCall(VirtualFrame frame, Object instance, PTuple clsTuple) {
1116-
Object state = IndirectCallContext.enter(frame, getContext(), this);
1117-
try {
1118-
return isInstanceRecursiveTruffleBoundary(instance, clsTuple);
1119-
} finally {
1120-
IndirectCallContext.exit(frame, getContext(), state);
1121-
}
1122-
}
1123-
1124-
@TruffleBoundary
1125-
boolean isInstanceRecursiveTruffleBoundary(Object instance, PTuple clsTuple) {
1126-
return isInstanceRecursiveNode(null, instance, clsTuple, this);
1127-
}
1128-
1129-
@Specialization(guards = {"!isPTuple(cls)", "!isPythonClass(cls)"})
1130-
static boolean isInstance(VirtualFrame frame, Object instance, Object cls,
1131-
@Shared("instanceCheck") @Cached("create(__INSTANCECHECK__)") LookupAndCallBinaryNode instanceCheckNode,
1132-
@Shared("boolCast") @Cached("createIfTrueNode()") CoerceToBooleanNode castToBooleanNode,
1133-
@Cached TypeBuiltins.InstanceCheckNode typeInstanceCheckNode) {
1134-
TriState check = isInstanceCheckInternal(frame, instance, cls, instanceCheckNode, castToBooleanNode);
1135-
if (check == TriState.UNDEFINED) {
1136-
return typeInstanceCheckNode.executeWith(frame, cls, instance);
1099+
@Specialization(guards = {"depth != STOP_RECURSION", "depth >= getNodeRecursionLimit()"})
1100+
final boolean doRecursiveWithLoop(VirtualFrame frame, Object instance, PTuple clsTuple,
1101+
@Cached("createRecursive(STOP_RECURSION)") RecursiveBinaryCheckBaseNode nonRecursiveNode) {
1102+
// Note: we do not put this behind TB to avoid frame materialization, an alternative can
1103+
// be to use IndirectCallNode, null frame and put this algorithm behind TB
1104+
ArrayDeque<Object> classes = PythonUtils.newDeque();
1105+
PythonUtils.push(classes, clsTuple);
1106+
while (!classes.isEmpty()) {
1107+
Object currentKlass = PythonUtils.pop(classes);
1108+
if (currentKlass instanceof PTuple) {
1109+
Object[] tupleItems = getArray((PTuple) currentKlass);
1110+
// push them in reverse order to pop them in the necessary order
1111+
for (int i = tupleItems.length - 1; i >= 0; i--) {
1112+
PythonUtils.push(classes, tupleItems[i]);
1113+
}
1114+
} else if (nonRecursiveNode.executeWith(frame, instance, currentKlass)) {
1115+
return true;
1116+
}
11371117
}
1138-
return check == TriState.TRUE;
1118+
return false;
11391119
}
11401120

1141-
protected int getLength(PTuple t) {
1121+
protected final int getLength(PTuple t) {
11421122
if (lenNode == null) {
11431123
CompilerDirectives.transferToInterpreterAndInvalidate();
11441124
lenNode = insert(SequenceStorageNodes.LenNode.create());
@@ -1155,78 +1135,78 @@ private Object[] getArray(PTuple tuple) {
11551135
}
11561136
}
11571137

1158-
// issubclass(class, classinfo)
1159-
@Builtin(name = ISSUBCLASS, minNumOfPositionalArgs = 2)
1138+
// isinstance(object, classinfo)
1139+
@Builtin(name = ISINSTANCE, minNumOfPositionalArgs = 2)
11601140
@GenerateNodeFactory
1161-
public abstract static class IsSubClassNode extends PythonBinaryBuiltinNode {
1162-
static final int MAX_EXPLODE_LOOP = 16; // is also divided by recursion depth+1
1141+
public abstract static class IsInstanceNode extends RecursiveBinaryCheckBaseNode {
11631142

1164-
@Child private SequenceStorageNodes.LenNode lenNode;
1165-
@Child private GetObjectArrayNode getObjectArrayNode;
1166-
protected final byte depth;
1167-
1168-
protected IsSubClassNode(byte depth) {
1169-
this.depth = depth;
1143+
protected IsInstanceNode(byte depth) {
1144+
super(depth);
11701145
}
11711146

1172-
protected IsSubClassNode() {
1147+
protected IsInstanceNode() {
11731148
this((byte) 0);
11741149
}
11751150

1176-
public static IsSubClassNode createRecursive(byte currentDepth) {
1177-
return BuiltinFunctionsFactory.IsSubClassNodeFactory.create((byte) (currentDepth + 1));
1151+
@Override
1152+
public IsInstanceNode createRecursive(byte newDepth) {
1153+
return BuiltinFunctionsFactory.IsInstanceNodeFactory.create(newDepth);
11781154
}
11791155

1180-
public abstract boolean executeWith(VirtualFrame frame, Object derived, Object cls);
1181-
1182-
private static boolean isSubclassCheckInternal(VirtualFrame frame, Object derived, Object cls, LookupAndCallBinaryNode subclassCheckNode, CoerceToBooleanNode castToBooleanNode) {
1183-
Object instanceCheckResult = subclassCheckNode.executeObject(frame, cls, derived);
1184-
return instanceCheckResult != NOT_IMPLEMENTED && castToBooleanNode.executeBoolean(frame, instanceCheckResult);
1156+
private static TriState isInstanceCheckInternal(VirtualFrame frame, Object instance, Object cls, LookupAndCallBinaryNode instanceCheckNode, CoerceToBooleanNode castToBooleanNode) {
1157+
Object instanceCheckResult = instanceCheckNode.executeObject(frame, cls, instance);
1158+
if (instanceCheckResult == NOT_IMPLEMENTED) {
1159+
return TriState.UNDEFINED;
1160+
}
1161+
return TriState.valueOf(castToBooleanNode.executeBoolean(frame, instanceCheckResult));
11851162
}
11861163

1187-
protected int getMaxExplodeLoop() {
1188-
return MAX_EXPLODE_LOOP / (depth + 1);
1164+
@Specialization(guards = "isPythonClass(cls)")
1165+
static boolean isInstance(VirtualFrame frame, Object instance, Object cls,
1166+
@Shared("instanceCheck") @Cached("create(__INSTANCECHECK__)") LookupAndCallBinaryNode instanceCheckNode,
1167+
@Shared("boolCast") @Cached("createIfTrueNode()") CoerceToBooleanNode castToBooleanNode,
1168+
@Cached GetClassNode getClassNode,
1169+
@Cached TypeNodes.IsSameTypeNode isSameTypeNode,
1170+
@Cached IsSubtypeNode isSubtypeNode) {
1171+
Object instanceClass = getClassNode.execute(instance);
1172+
return isSameTypeNode.execute(instanceClass, cls) || isSubtypeNode.execute(frame, instanceClass, cls)//
1173+
|| isInstanceCheckInternal(frame, instance, cls, instanceCheckNode, castToBooleanNode) == TriState.TRUE;
11891174
}
11901175

1191-
@Specialization(guards = {"depth < getNodeRecursionLimit()", "getLength(clsTuple) == cachedLen", "cachedLen <= getMaxExplodeLoop()"}, limit = "getVariableArgumentInlineCacheLimit()")
1192-
@ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN)
1193-
boolean isSubclassTupleConstantLen(VirtualFrame frame, Object derived, PTuple clsTuple,
1194-
@Cached("getLength(clsTuple)") int cachedLen,
1195-
@Cached("createRecursive(depth)") IsSubClassNode isSubclassNode) {
1196-
Object[] array = getArray(clsTuple);
1197-
for (int i = 0; i < cachedLen; i++) {
1198-
Object cls = array[i];
1199-
if (isSubclassNode.executeWith(frame, derived, cls)) {
1200-
return true;
1201-
}
1176+
@Specialization(guards = {"!isPTuple(cls)", "!isPythonClass(cls)"})
1177+
static boolean isInstance(VirtualFrame frame, Object instance, Object cls,
1178+
@Shared("instanceCheck") @Cached("create(__INSTANCECHECK__)") LookupAndCallBinaryNode instanceCheckNode,
1179+
@Shared("boolCast") @Cached("createIfTrueNode()") CoerceToBooleanNode castToBooleanNode,
1180+
@Cached TypeBuiltins.InstanceCheckNode typeInstanceCheckNode) {
1181+
TriState check = isInstanceCheckInternal(frame, instance, cls, instanceCheckNode, castToBooleanNode);
1182+
if (check == TriState.UNDEFINED) {
1183+
return typeInstanceCheckNode.executeWith(frame, cls, instance);
12021184
}
1203-
return false;
1185+
return check == TriState.TRUE;
12041186
}
1187+
}
12051188

1206-
@Specialization(guards = "depth < getNodeRecursionLimit()", replaces = "isSubclassTupleConstantLen")
1207-
boolean isSubclassRecursiveNode(VirtualFrame frame, Object derived, PTuple clsTuple,
1208-
@Cached("createRecursive(depth)") IsSubClassNode isSubclassNode) {
1209-
for (Object cls : getArray(clsTuple)) {
1210-
if (isSubclassNode.executeWith(frame, derived, cls)) {
1211-
return true;
1212-
}
1213-
}
1214-
return false;
1189+
// issubclass(class, classinfo)
1190+
@Builtin(name = ISSUBCLASS, minNumOfPositionalArgs = 2)
1191+
@GenerateNodeFactory
1192+
public abstract static class IsSubClassNode extends RecursiveBinaryCheckBaseNode {
1193+
1194+
protected IsSubClassNode(byte depth) {
1195+
super(depth);
12151196
}
12161197

1217-
@Specialization(guards = "depth >= getNodeRecursionLimit()")
1218-
boolean isSubclassRecursiveCall(VirtualFrame frame, Object derived, PTuple clsTuple) {
1219-
Object state = IndirectCallContext.enter(frame, getContext(), this);
1220-
try {
1221-
return isSubclassRecursiveTruffleBoundary(derived, clsTuple);
1222-
} finally {
1223-
IndirectCallContext.exit(frame, getContext(), state);
1224-
}
1198+
protected IsSubClassNode() {
1199+
this((byte) 0);
12251200
}
12261201

1227-
@TruffleBoundary
1228-
private boolean isSubclassRecursiveTruffleBoundary(Object derived, PTuple clsTuple) {
1229-
return isSubclassRecursiveNode(null, derived, clsTuple, this);
1202+
@Override
1203+
public IsSubClassNode createRecursive(byte newDepth) {
1204+
return BuiltinFunctionsFactory.IsSubClassNodeFactory.create(newDepth);
1205+
}
1206+
1207+
private static boolean isSubclassCheckInternal(VirtualFrame frame, Object derived, Object cls, LookupAndCallBinaryNode subclassCheckNode, CoerceToBooleanNode castToBooleanNode) {
1208+
Object instanceCheckResult = subclassCheckNode.executeObject(frame, cls, derived);
1209+
return instanceCheckResult != NOT_IMPLEMENTED && castToBooleanNode.executeBoolean(frame, instanceCheckResult);
12301210
}
12311211

12321212
@Specialization(guards = "!isPTuple(cls)")
@@ -1236,22 +1216,6 @@ static boolean isSubclass(VirtualFrame frame, Object derived, Object cls,
12361216
@Cached IsSubtypeNode isSubtypeNode) {
12371217
return isSubclassCheckInternal(frame, derived, cls, subclassCheckNode, castToBooleanNode) || isSubtypeNode.execute(frame, derived, cls);
12381218
}
1239-
1240-
protected int getLength(PTuple t) {
1241-
if (lenNode == null) {
1242-
CompilerDirectives.transferToInterpreterAndInvalidate();
1243-
lenNode = insert(SequenceStorageNodes.LenNode.create());
1244-
}
1245-
return lenNode.execute(t.getSequenceStorage());
1246-
}
1247-
1248-
private Object[] getArray(PTuple tuple) {
1249-
if (getObjectArrayNode == null) {
1250-
CompilerDirectives.transferToInterpreterAndInvalidate();
1251-
getObjectArrayNode = insert(GetObjectArrayNodeGen.create());
1252-
}
1253-
return getObjectArrayNode.execute(tuple);
1254-
}
12551219
}
12561220

12571221
// iter(object[, sentinel])

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,10 @@ public static int getVariableArgumentInlineCacheLimit() {
362362

363363
public static int getNodeRecursionLimit() {
364364
CompilerAsserts.neverPartOfCompilation();
365-
return PythonLanguage.getCurrent().getEngineOption(NodeRecursionLimit);
365+
int result = PythonLanguage.getCurrent().getEngineOption(NodeRecursionLimit);
366+
// So that we can use byte counters and also Byte.MAX_VALUE as special placeholder
367+
assert result < Byte.MAX_VALUE;
368+
return result;
366369
}
367370

368371
public static boolean isWithJavaStacktrace(PythonLanguage language) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.lang.management.ManagementFactory;
4444
import java.nio.ByteBuffer;
4545
import java.nio.ByteOrder;
46+
import java.util.ArrayDeque;
4647
import java.util.Arrays;
4748

4849
import javax.management.InstanceNotFoundException;
@@ -446,4 +447,19 @@ public static int getBufferRemaining(ByteBuffer buffer) {
446447
public static boolean bufferHasRemaining(ByteBuffer buffer) {
447448
return buffer.hasRemaining();
448449
}
450+
451+
@TruffleBoundary
452+
public static <E> ArrayDeque<E> newDeque() {
453+
return new ArrayDeque<>();
454+
}
455+
456+
@TruffleBoundary
457+
public static <E> void push(ArrayDeque<E> q, E e) {
458+
q.push(e);
459+
}
460+
461+
@TruffleBoundary
462+
public static <E> E pop(ArrayDeque<E> q) {
463+
return q.pop();
464+
}
449465
}

0 commit comments

Comments
 (0)