Skip to content

Commit 3567276

Browse files
msimaceksteve-s
authored andcommitted
Use a control flow exception to communicate iterator exhaustion
1 parent fab3eb0 commit 3567276

File tree

80 files changed

+767
-575
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+767
-575
lines changed

Diff for: graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/datatype/PRangeTests.java

+13-10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.oracle.graal.python.PythonLanguage;
3434
import com.oracle.graal.python.builtins.objects.range.PIntRange;
3535
import com.oracle.graal.python.builtins.objects.range.PRange;
36+
import com.oracle.graal.python.lib.IteratorExhausted;
3637
import com.oracle.graal.python.lib.PyIterNextNode;
3738
import com.oracle.graal.python.lib.PyObjectGetIter;
3839
import com.oracle.graal.python.nodes.PGuards;
@@ -69,13 +70,14 @@ public void loopWithOnlyStop() throws UnexpectedResultException {
6970
Object iter = PyObjectGetIter.executeUncached(range);
7071

7172
while (true) {
72-
Object next = PyIterNextNode.executeUncached(iter);
73-
if (PyIterNextNode.isExhausted(next)) {
73+
try {
74+
Object next = PyIterNextNode.executeUncached(iter);
75+
int item = PGuards.expectInteger(next);
76+
assertEquals(index, item);
77+
index++;
78+
} catch (IteratorExhausted e) {
7479
break;
7580
}
76-
int item = PGuards.expectInteger(next);
77-
assertEquals(index, item);
78-
index++;
7981
}
8082
} finally {
8183
PythonTests.closeContext();
@@ -92,13 +94,14 @@ public void loopWithStep() throws UnexpectedResultException {
9294
Object iter = PyObjectGetIter.executeUncached(range);
9395

9496
while (true) {
95-
Object next = PyIterNextNode.executeUncached(iter);
96-
if (PyIterNextNode.isExhausted(next)) {
97+
try {
98+
Object next = PyIterNextNode.executeUncached(iter);
99+
int item = PGuards.expectInteger(next);
100+
assertEquals(index, item);
101+
index += 2;
102+
} catch (IteratorExhausted e) {
97103
break;
98104
}
99-
int item = PGuards.expectInteger(next);
100-
assertEquals(index, item);
101-
index += 2;
102105
}
103106
} finally {
104107
PythonTests.closeContext();

Diff for: graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ArrayModuleBuiltins.java

+12-10
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import com.oracle.graal.python.builtins.objects.range.PIntRange;
6262
import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode;
6363
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
64+
import com.oracle.graal.python.lib.IteratorExhausted;
6465
import com.oracle.graal.python.lib.PyIterNextNode;
6566
import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs;
6667
import com.oracle.graal.python.lib.PyObjectGetIter;
@@ -295,18 +296,19 @@ static PArray arrayIteratorInitializer(VirtualFrame frame, Node inliningTarget,
295296

296297
int length = 0;
297298
while (true) {
298-
Object nextValue = nextNode.execute(frame, inliningTarget, iter);
299-
if (PyIterNextNode.isExhausted(nextValue)) {
300-
break;
301-
}
302299
try {
303-
length = PythonUtils.addExact(length, 1);
304-
ensureCapacityNode.execute(inliningTarget, array, length);
305-
} catch (OverflowException e) {
306-
CompilerDirectives.transferToInterpreterAndInvalidate();
307-
throw PRaiseNode.raiseStatic(inliningTarget, MemoryError);
300+
Object nextValue = nextNode.execute(frame, inliningTarget, iter);
301+
try {
302+
length = PythonUtils.addExact(length, 1);
303+
ensureCapacityNode.execute(inliningTarget, array, length);
304+
} catch (OverflowException e) {
305+
CompilerDirectives.transferToInterpreterAndInvalidate();
306+
throw PRaiseNode.raiseStatic(inliningTarget, MemoryError);
307+
}
308+
putValueNode.execute(frame, inliningTarget, array, length - 1, nextValue);
309+
} catch (IteratorExhausted e) {
310+
break;
308311
}
309-
putValueNode.execute(frame, inliningTarget, array, length - 1, nextValue);
310312
}
311313

312314
setLengthNode.execute(inliningTarget, array, length);

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

+92-65
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@
168168
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.CallSlotTpIterNextNode;
169169
import com.oracle.graal.python.compiler.Compiler;
170170
import com.oracle.graal.python.compiler.RaisePythonExceptionErrorCallback;
171+
import com.oracle.graal.python.lib.IteratorExhausted;
171172
import com.oracle.graal.python.lib.PyCallableCheckNode;
172173
import com.oracle.graal.python.lib.PyEvalGetGlobals;
173174
import com.oracle.graal.python.lib.PyEvalGetLocals;
@@ -486,13 +487,12 @@ static boolean doObject(VirtualFrame frame, Object object,
486487
while (true) {
487488
try {
488489
Object next = nextNode.execute(frame, inliningTarget, iterator);
489-
if (PyIterNextNode.isExhausted(next)) {
490-
break;
491-
}
492490
nbrIter++;
493491
if (!isTrueNode.execute(frame, next)) {
494492
return false;
495493
}
494+
} catch (IteratorExhausted e) {
495+
break;
496496
} finally {
497497
LoopNode.reportLoopCount(inliningTarget, nbrIter);
498498
}
@@ -538,13 +538,12 @@ static boolean doObject(VirtualFrame frame, Object object,
538538
while (true) {
539539
try {
540540
Object next = nextNode.execute(frame, inliningTarget, iterator);
541-
if (PyIterNextNode.isExhausted(next)) {
542-
break;
543-
}
544541
nbrIter++;
545542
if (isTrueNode.execute(frame, next)) {
546543
return true;
547544
}
545+
} catch (IteratorExhausted e) {
546+
break;
548547
} finally {
549548
LoopNode.reportLoopCount(inliningTarget, nbrIter);
550549
}
@@ -1583,8 +1582,10 @@ static Object minmaxSequenceWithKey(VirtualFrame frame, Node inliningTarget, Obj
15831582
Object keywordArg = kwArgsAreNone ? null : keywordArgIn;
15841583

15851584
Object iterator = getIter.execute(frame, inliningTarget, arg1);
1586-
Object currentValue = nextNode.execute(frame, inliningTarget, iterator);
1587-
if (PyIterNextNode.isExhausted(currentValue)) {
1585+
Object currentValue;
1586+
try {
1587+
currentValue = nextNode.execute(frame, inliningTarget, iterator);
1588+
} catch (IteratorExhausted e) {
15881589
if (hasDefaultProfile.profile(inliningTarget, isNoValue(defaultVal))) {
15891590
throw raiseNode.raise(inliningTarget, PythonErrorType.ValueError, ErrorMessages.ARG_IS_EMPTY_SEQ, name);
15901591
} else {
@@ -1593,12 +1594,9 @@ static Object minmaxSequenceWithKey(VirtualFrame frame, Node inliningTarget, Obj
15931594
}
15941595
Object currentKey = applyKeyFunction(frame, inliningTarget, keywordArg, keyCall, currentValue);
15951596
int loopCount = 0;
1596-
try {
1597-
while (true) {
1597+
while (true) {
1598+
try {
15981599
Object nextValue = nextNode.execute(frame, inliningTarget, iterator);
1599-
if (PyIterNextNode.isExhausted(nextValue)) {
1600-
break;
1601-
}
16021600
Object nextKey = applyKeyFunction(frame, inliningTarget, keywordArg, keyCall, nextValue);
16031601
boolean isTrue;
16041602
if (!seenNonBoolean.wasEntered(inliningTarget)) {
@@ -1616,10 +1614,13 @@ static Object minmaxSequenceWithKey(VirtualFrame frame, Node inliningTarget, Obj
16161614
currentValue = nextValue;
16171615
}
16181616
loopCount++;
1617+
} catch (IteratorExhausted e) {
1618+
break;
1619+
} finally {
1620+
LoopNode.reportLoopCount(inliningTarget, loopCount < 0 ? Integer.MAX_VALUE : loopCount);
16191621
}
1620-
} finally {
1621-
LoopNode.reportLoopCount(inliningTarget, loopCount < 0 ? Integer.MAX_VALUE : loopCount);
16221622
}
1623+
16231624
return currentValue;
16241625
}
16251626

@@ -1737,29 +1738,26 @@ static Object next(VirtualFrame frame, Object iterator, Object defaultObject,
17371738
if (!PyIterCheckNode.checkSlots(slots)) {
17381739
throw raiseTypeError.raise(inliningTarget, TypeError, ErrorMessages.OBJ_ISNT_ITERATOR, iterator);
17391740
}
1740-
Object result;
17411741
try {
1742-
result = callIterNext.execute(frame, inliningTarget, slots.tp_iternext(), iterator);
1743-
} catch (PException e) {
1742+
return callIterNext.execute(frame, inliningTarget, slots.tp_iternext(), iterator);
1743+
} catch (IteratorExhausted e) {
17441744
if (defaultIsNoValue.profile(inliningTarget, defaultObject == NO_VALUE)) {
1745-
throw e;
1745+
throw raiseStopIteration.raise(inliningTarget, StopIteration);
17461746
} else {
1747-
e.expectStopIteration(inliningTarget, stopIterationProfile);
17481747
return defaultObject;
17491748
}
1750-
}
1751-
if (PyIterNextNode.isExhausted(result)) {
1749+
} catch (PException e) {
17521750
if (defaultIsNoValue.profile(inliningTarget, defaultObject == NO_VALUE)) {
1753-
throw raiseStopIteration.raise(inliningTarget, StopIteration);
1751+
throw e;
17541752
} else {
1753+
e.expectStopIteration(inliningTarget, stopIterationProfile);
17551754
return defaultObject;
17561755
}
17571756
}
1758-
return result;
17591757
}
17601758
}
17611759

1762-
// ord(c)
1760+
// ord(c)
17631761
@Builtin(name = J_ORD, minNumOfPositionalArgs = 1)
17641762
@GenerateNodeFactory
17651763
@ImportStatic(PGuards.class)
@@ -1805,7 +1803,7 @@ static Object ord(@SuppressWarnings("unused") Object obj,
18051803
}
18061804
}
18071805

1808-
// print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
1806+
// print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
18091807
@Builtin(name = J_PRINT, takesVarArgs = true, keywordOnlyNames = {"sep", "end", "file", "flush"}, doc = "\n" +
18101808
"print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n" +
18111809
"\n" +
@@ -2219,61 +2217,90 @@ static Object sumGeneric(VirtualFrame frame, Node inliningTarget, Object iterato
22192217
@Cached PyIterNextNode nextNode,
22202218
@Shared @Cached PyNumberAddNode addNode,
22212219
@Shared @Cached InlinedConditionProfile resultFitsInInt,
2220+
@Exclusive @Cached InlinedBranchProfile seenObject,
22222221
@Exclusive @Cached InlinedBranchProfile seenInt,
22232222
@Exclusive @Cached InlinedBranchProfile seenDouble,
2224-
@Exclusive @Cached InlinedBranchProfile seenObject) {
2223+
@Exclusive @Cached InlinedBranchProfile genericBranch) {
22252224
/*
22262225
* Peel the first iteration to see what's the type.
22272226
*/
2228-
Object next = nextNode.execute(frame, inliningTarget, iterator);
2229-
if (!PyIterNextNode.isExhausted(next)) {
2230-
Object acc = addNode.execute(frame, inliningTarget, start, next);
2231-
/*
2232-
* We try to process integers/longs/doubles as long as we can. Then we always
2233-
* fall through to the generic path. `next` and `acc` are always properly set so
2234-
* that the generic path can check if there are remaining items and resume if
2235-
* necessary.
2236-
*/
2237-
if (acc instanceof Integer || acc instanceof Long) {
2238-
seenInt.enter(inliningTarget);
2239-
long longAcc = acc instanceof Integer ? (int) acc : (long) acc;
2240-
while (loopProfilePrimitive.profile(inliningTarget, !PyIterNextNode.isExhausted(next = nextNode.execute(frame, inliningTarget, iterator)))) {
2241-
try {
2242-
if (next instanceof Integer nextInt) {
2243-
longAcc = PythonUtils.addExact(longAcc, nextInt);
2244-
} else if (next instanceof Long nextLong) {
2245-
longAcc = PythonUtils.addExact(longAcc, nextLong);
2246-
} else {
2247-
break;
2248-
}
2249-
} catch (OverflowException e) {
2250-
break;
2227+
Object next;
2228+
try {
2229+
next = nextNode.execute(frame, inliningTarget, iterator);
2230+
} catch (IteratorExhausted e) {
2231+
return start;
2232+
}
2233+
Object acc = addNode.execute(frame, inliningTarget, start, next);
2234+
/*
2235+
* We try to process integers/longs/doubles as long as we can. Then we always fall
2236+
* through to the generic path. `next` and `acc` are always properly set so that the
2237+
* generic path can check if there are remaining items and resume if necessary.
2238+
*/
2239+
if (acc instanceof Integer || acc instanceof Long) {
2240+
seenInt.enter(inliningTarget);
2241+
long longAcc = acc instanceof Integer ? (int) acc : (long) acc;
2242+
boolean exitLoop = false, exhausted = false;
2243+
while (loopProfilePrimitive.profile(inliningTarget, !exitLoop)) {
2244+
try {
2245+
next = nextNode.execute(frame, inliningTarget, iterator);
2246+
if (next instanceof Integer nextInt) {
2247+
longAcc = PythonUtils.addExact(longAcc, nextInt);
2248+
} else if (next instanceof Long nextLong) {
2249+
longAcc = PythonUtils.addExact(longAcc, nextLong);
2250+
} else {
2251+
exitLoop = true;
22512252
}
2253+
} catch (OverflowException e) {
2254+
exitLoop = true;
2255+
} catch (IteratorExhausted e) {
2256+
exitLoop = true;
2257+
exhausted = true;
22522258
}
2253-
acc = maybeInt(inliningTarget, resultFitsInInt, longAcc);
2254-
} else if (acc instanceof Double doubleAcc) {
2255-
seenDouble.enter(inliningTarget);
2256-
while (loopProfilePrimitive.profile(inliningTarget, !PyIterNextNode.isExhausted(next = nextNode.execute(frame, inliningTarget, iterator)))) {
2259+
}
2260+
if (exhausted) {
2261+
return maybeInt(inliningTarget, resultFitsInInt, longAcc);
2262+
}
2263+
genericBranch.enter(inliningTarget);
2264+
acc = longAcc;
2265+
} else if (acc instanceof Double doubleAcc) {
2266+
seenDouble.enter(inliningTarget);
2267+
boolean exitLoop = false, exhausted = false;
2268+
while (loopProfilePrimitive.profile(inliningTarget, !exitLoop)) {
2269+
try {
2270+
next = nextNode.execute(frame, inliningTarget, iterator);
22572271
if (next instanceof Double nextDouble) {
22582272
doubleAcc += nextDouble;
22592273
} else {
2260-
break;
2274+
exitLoop = true;
22612275
}
2276+
} catch (IteratorExhausted e) {
2277+
exitLoop = true;
2278+
exhausted = true;
22622279
}
2263-
acc = doubleAcc;
2264-
} else {
2265-
next = nextNode.execute(frame, inliningTarget, iterator);
22662280
}
2267-
if (!PyIterNextNode.isExhausted(next)) {
2268-
seenObject.enter(inliningTarget);
2269-
do {
2270-
acc = addNode.execute(frame, inliningTarget, acc, next);
2271-
} while (loopProfileGeneric.profile(inliningTarget, !PyIterNextNode.isExhausted(next = nextNode.execute(frame, inliningTarget, iterator))));
2281+
if (exhausted) {
2282+
return doubleAcc;
22722283
}
2273-
return acc;
2284+
genericBranch.enter(inliningTarget);
2285+
acc = doubleAcc;
22742286
} else {
2275-
return start;
2287+
seenObject.enter(inliningTarget);
2288+
try {
2289+
next = nextNode.execute(frame, inliningTarget, iterator);
2290+
} catch (IteratorExhausted e) {
2291+
return acc;
2292+
}
22762293
}
2294+
boolean exhausted = false;
2295+
do {
2296+
acc = addNode.execute(frame, inliningTarget, acc, next);
2297+
try {
2298+
next = nextNode.execute(frame, inliningTarget, iterator);
2299+
} catch (IteratorExhausted e) {
2300+
exhausted = true;
2301+
}
2302+
} while (loopProfileGeneric.profile(inliningTarget, !exhausted));
2303+
return acc;
22772304
}
22782305

22792306
private static long maybeInt(Node inliningTarget, InlinedConditionProfile resultFitsInInt, long result) {

0 commit comments

Comments
 (0)