Skip to content

Commit 194fbc5

Browse files
committed
[GR-65693] Array iteration using for-of slower than classical for loop.
PullRequest: js/3555
2 parents 1460b43 + a6e666d commit 194fbc5

File tree

9 files changed

+342
-216
lines changed

9 files changed

+342
-216
lines changed

graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/ir/ForNode.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -240,9 +240,9 @@ public boolean isForAwaitOf() {
240240
}
241241

242242
/**
243-
* Is this a for-in or for-of statement?
243+
* Is this a for-in, for-of, or for-await-of statement?
244244
*
245-
* @return true if this is a for-in or for-of loop
245+
* @return true if this is a for-in, for-of, or for-await-of loop
246246
*/
247247
public boolean isForInOrOf() {
248248
return isForIn() || isForOf() || isForAwaitOf();

graal-js/src/com.oracle.truffle.js.parser/src/com/oracle/truffle/js/parser/GraalJSTranslator.java

Lines changed: 78 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,43 +2130,83 @@ private JavaScriptNode desugarForOf(ForNode forNode, JavaScriptNode modify, Jump
21302130
return desugarForInOrOfBody(forNode, getIterator, jumpTarget);
21312131
}
21322132

2133+
private JavaScriptNode desugarForAwaitOf(ForNode forNode, JavaScriptNode modify, JumpTargetCloseable<ContinueTarget> jumpTarget) {
2134+
assert forNode.isForAwaitOf();
2135+
JavaScriptNode getIterator = factory.createGetAsyncIterator(modify);
2136+
return desugarForInOrOfBody(forNode, getIterator, jumpTarget);
2137+
}
2138+
2139+
/**
2140+
* Desugars a for in/of/await-of loop body.
2141+
*
2142+
* <code>
2143+
* <pre>
2144+
* var iterator = ForIn/OfHeadEvaluation();
2145+
* break: while (true) {
2146+
* var nextResult = IteratorNext(iterator);
2147+
* var done = IteratorComplete(nextResult);
2148+
* var nextValue = IteratorValue(nextResult);
2149+
* if (done) break;
2150+
* continue: {{ // per-iteration scope
2151+
* let [[binding]];
2152+
* try {
2153+
* [[binding]] := nextValue, nextValue = undefined;
2154+
* [[for:body]];
2155+
* } catch (e) {
2156+
* IteratorClose(iterator, e);
2157+
* }
2158+
* }}
2159+
* }
2160+
* </pre>
2161+
* </code>
2162+
*/
21332163
private JavaScriptNode desugarForInOrOfBody(ForNode forNode, JavaScriptNode iterator, JumpTargetCloseable<ContinueTarget> jumpTarget) {
21342164
assert forNode.isForInOrOf();
21352165
VarRef iteratorVar = environment.createTempVar();
2136-
JavaScriptNode iteratorInit = iteratorVar.createWriteNode(iterator);
21372166
VarRef nextResultVar = environment.createTempVar();
2138-
JavaScriptNode iteratorNext = factory.createIteratorNext(iteratorVar.createReadNode());
2139-
// nextResult = IteratorNext(iterator)
2140-
// while(!(done = IteratorComplete(nextResult)))
2141-
JavaScriptNode condition = factory.createDual(context,
2142-
factory.createIteratorSetDone(iteratorVar.createReadNode(), factory.createConstantBoolean(true)),
2143-
factory.createUnary(UnaryOperation.NOT, factory.createIteratorComplete(context, nextResultVar.createWriteNode(iteratorNext))));
2167+
JavaScriptNode iteratorInit = iteratorVar.createWriteNode(iterator);
2168+
2169+
JavaScriptNode iteratorNext;
2170+
if (forNode.isForAwaitOf()) {
2171+
// nextResult = Await(IteratorNext(iterator))
2172+
iteratorNext = awaitAsyncIteratorNext(iteratorVar.createReadNode());
2173+
} else {
2174+
// nextResult = IteratorNext(iterator)
2175+
iteratorNext = factory.createIteratorNext(iteratorVar.createReadNode());
2176+
}
2177+
21442178
JavaScriptNode wrappedBody;
21452179
try (EnvironmentCloseable blockEnv = new EnvironmentCloseable(needsPerIterationScope(forNode) ? newPerIterationEnvironment(lc.getCurrentBlock().getScope()) : environment)) {
2146-
// var nextValue = IteratorValue(nextResult);
2147-
VarRef nextResultVar2 = environment.findTempVar(nextResultVar.getFrameSlot());
2148-
VarRef nextValueVar = environment.createTempVar();
2149-
VarRef iteratorVar2 = environment.findTempVar(iteratorVar.getFrameSlot());
2150-
JavaScriptNode nextResult = nextResultVar2.createReadNode();
2151-
JavaScriptNode nextValue = factory.createIteratorValue(nextResult);
2152-
JavaScriptNode writeNextValue = nextValueVar.createWriteNode(nextValue);
2153-
JavaScriptNode writeNext = tagStatement(desugarForHeadAssignment(forNode, nextValueVar.createReadNode()), forNode);
2180+
VarRef nextValueVarInner = environment.findTempVar(nextResultVar.getFrameSlot());
2181+
JavaScriptNode nextValue = nextValueVarInner.createReadNode();
2182+
JavaScriptNode nextBindingInit = tagStatement(desugarForHeadAssignment(forNode, nextValue), forNode);
2183+
JavaScriptNode clearTempSlot = nextValueVarInner.createWriteNode(factory.createConstant(JSFrameUtil.DEFAULT_VALUE));
21542184
JavaScriptNode body = transform(forNode.getBody());
21552185
wrappedBody = blockEnv.wrapBlockScope(createBlock(
2156-
writeNextValue,
2157-
factory.createIteratorSetDone(iteratorVar2.createReadNode(), factory.createConstantBoolean(false)),
2158-
writeNext,
2186+
factory.createTryFinally(nextBindingInit, clearTempSlot),
21592187
body));
21602188
}
21612189
wrappedBody = jumpTarget.wrapContinueTargetNode(wrappedBody);
2162-
RepeatingNode repeatingNode = factory.createWhileDoRepeatingNode(condition, wrappedBody);
2190+
2191+
if (forNode.isForAwaitOf()) {
2192+
wrappedBody = wrapAsyncIteratorClose(wrappedBody, iteratorVar.createReadNode());
2193+
} else {
2194+
wrappedBody = factory.createIteratorCloseWrapper(context, wrappedBody, iteratorVar.createReadNode(), false);
2195+
}
2196+
2197+
RepeatingNode repeatingNode = factory.createForOfRepeatingNode(iteratorNext, wrappedBody,
2198+
(JSWriteFrameSlotNode) nextResultVar.createWriteNode(null));
21632199
LoopNode loopNode = factory.createLoopNode(repeatingNode);
2164-
JavaScriptNode whileNode = forNode.isForOf() ? factory.createDesugaredForOf(loopNode) : factory.createDesugaredForIn(loopNode);
2165-
JavaScriptNode wrappedWhile = factory.createIteratorCloseIfNotDone(context, jumpTarget.wrapBreakTargetNode(whileNode), iteratorVar.createReadNode());
2166-
JavaScriptNode resetIterator = iteratorVar.createWriteNode(factory.createConstant(JSFrameUtil.DEFAULT_VALUE));
2167-
wrappedWhile = factory.createTryFinally(wrappedWhile, resetIterator);
2200+
JavaScriptNode whileNode = forNode.isForOf()
2201+
? forNode.isForAwaitOf()
2202+
? factory.createDesugaredForAwaitOf(loopNode)
2203+
: factory.createDesugaredForOf(loopNode)
2204+
: factory.createDesugaredForIn(loopNode);
21682205
ensureHasSourceSection(whileNode, forNode);
2169-
return createBlock(iteratorInit, wrappedWhile);
2206+
2207+
JavaScriptNode wrappedWhile = jumpTarget.wrapBreakTargetNode(whileNode);
2208+
JavaScriptNode resetIterator = iteratorVar.createWriteNode(factory.createConstant(JSFrameUtil.DEFAULT_VALUE));
2209+
return createBlock(iteratorInit, factory.createTryFinally(wrappedWhile, resetIterator));
21702210
}
21712211

21722212
private JavaScriptNode desugarForHeadAssignment(ForNode forNode, JavaScriptNode next) {
@@ -2179,54 +2219,24 @@ private JavaScriptNode desugarForHeadAssignment(ForNode forNode, JavaScriptNode
21792219
}
21802220
}
21812221

2182-
private JavaScriptNode desugarForAwaitOf(ForNode forNode, JavaScriptNode modify, JumpTargetCloseable<ContinueTarget> jumpTarget) {
2183-
assert forNode.isForAwaitOf();
2184-
JavaScriptNode getIterator = factory.createGetAsyncIterator(modify);
2185-
VarRef iteratorVar = environment.createTempVar();
2186-
JavaScriptNode iteratorInit = iteratorVar.createWriteNode(getIterator);
2187-
VarRef nextResultVar = environment.createTempVar();
2188-
2222+
private JavaScriptNode awaitAsyncIteratorNext(JavaScriptNode iterator) {
21892223
currentFunction().addAwait();
2190-
JSReadFrameSlotNode asyncResultNode = (JSReadFrameSlotNode) environment.findTempVar(currentFunction().getAsyncResultSlot()).createReadNode();
2191-
JSReadFrameSlotNode asyncContextNode = (JSReadFrameSlotNode) environment.findTempVar(currentFunction().getAsyncContextSlot()).createReadNode();
21922224
JSFrameDescriptor functionFrameDesc = environment.getFunctionFrameDescriptor();
2193-
JSFrameSlot stateSlot = addGeneratorStateSlot(functionFrameDesc, FrameSlotKind.Int);
2194-
JavaScriptNode iteratorNext = factory.createAsyncIteratorNext(context, stateSlot, iteratorVar.createReadNode(),
2225+
var iterNextStateSlot = addGeneratorStateSlot(functionFrameDesc, FrameSlotKind.Int);
2226+
var asyncResultNode = (JSReadFrameSlotNode) environment.findTempVar(currentFunction().getAsyncResultSlot()).createReadNode();
2227+
var asyncContextNode = (JSReadFrameSlotNode) environment.findTempVar(currentFunction().getAsyncContextSlot()).createReadNode();
2228+
return factory.createAsyncIteratorNext(context, iterNextStateSlot, iterator,
21952229
asyncContextNode, asyncResultNode);
2196-
// nextResult = Await(IteratorNext(iterator))
2197-
// while(!(done = IteratorComplete(nextResult)))
2198-
JavaScriptNode condition = factory.createDual(context,
2199-
factory.createIteratorSetDone(iteratorVar.createReadNode(), factory.createConstantBoolean(true)),
2200-
factory.createUnary(UnaryOperation.NOT, factory.createIteratorComplete(context, nextResultVar.createWriteNode(iteratorNext))));
2201-
JavaScriptNode wrappedBody;
2202-
try (EnvironmentCloseable blockEnv = new EnvironmentCloseable(needsPerIterationScope(forNode) ? newPerIterationEnvironment(lc.getCurrentBlock().getScope()) : environment)) {
2203-
// var nextValue = IteratorValue(nextResult);
2204-
VarRef nextResultVar2 = environment.findTempVar(nextResultVar.getFrameSlot());
2205-
VarRef nextValueVar = environment.createTempVar();
2206-
VarRef iteratorVar2 = environment.findTempVar(iteratorVar.getFrameSlot());
2207-
JavaScriptNode nextResult = nextResultVar2.createReadNode();
2208-
JavaScriptNode nextValue = factory.createIteratorValue(nextResult);
2209-
JavaScriptNode writeNextValue = nextValueVar.createWriteNode(nextValue);
2210-
JavaScriptNode writeNext = tagStatement(desugarForHeadAssignment(forNode, nextValueVar.createReadNode()), forNode);
2211-
JavaScriptNode body = transform(forNode.getBody());
2212-
wrappedBody = blockEnv.wrapBlockScope(createBlock(
2213-
writeNextValue,
2214-
factory.createIteratorSetDone(iteratorVar2.createReadNode(), factory.createConstantBoolean(false)),
2215-
writeNext,
2216-
body));
2217-
}
2218-
wrappedBody = jumpTarget.wrapContinueTargetNode(wrappedBody);
2219-
RepeatingNode repeatingNode = factory.createWhileDoRepeatingNode(condition, wrappedBody);
2220-
LoopNode loopNode = factory.createLoopNode(repeatingNode);
2221-
JavaScriptNode whileNode = factory.createDesugaredForAwaitOf(loopNode);
2230+
}
2231+
2232+
private JavaScriptNode wrapAsyncIteratorClose(JavaScriptNode body, JavaScriptNode iterator) {
22222233
currentFunction().addAwait();
2223-
stateSlot = addGeneratorStateSlot(functionFrameDesc, FrameSlotKind.Object);
2224-
JavaScriptNode wrappedWhile = factory.createAsyncIteratorCloseWrapper(context, stateSlot, jumpTarget.wrapBreakTargetNode(whileNode), iteratorVar.createReadNode(),
2234+
JSFrameDescriptor functionFrameDesc = environment.getFunctionFrameDescriptor();
2235+
var iterCloseStateSlot = addGeneratorStateSlot(functionFrameDesc, FrameSlotKind.Object);
2236+
var asyncResultNode = (JSReadFrameSlotNode) environment.findTempVar(currentFunction().getAsyncResultSlot()).createReadNode();
2237+
var asyncContextNode = (JSReadFrameSlotNode) environment.findTempVar(currentFunction().getAsyncContextSlot()).createReadNode();
2238+
return factory.createAsyncIteratorCloseWrapper(context, iterCloseStateSlot, body, iterator,
22252239
asyncContextNode, asyncResultNode);
2226-
JavaScriptNode resetIterator = iteratorVar.createWriteNode(factory.createConstant(JSFrameUtil.DEFAULT_VALUE));
2227-
wrappedWhile = factory.createTryFinally(wrappedWhile, resetIterator);
2228-
ensureHasSourceSection(whileNode, forNode);
2229-
return createBlock(iteratorInit, wrappedWhile);
22302240
}
22312241

22322242
private boolean needsPerIterationScope(ForNode forNode) {
@@ -3093,7 +3103,7 @@ private JavaScriptNode transformDestructuringArrayAssignment(Expression lhsExpre
30933103
initElements[i] = rhsNode;
30943104
}
30953105
}
3096-
JavaScriptNode closeIfNotDone = factory.createIteratorCloseIfNotDone(context, createBlock(initElements), iteratorTempVar.createReadNode());
3106+
JavaScriptNode closeIfNotDone = factory.createIteratorCloseWrapper(context, createBlock(initElements), iteratorTempVar.createReadNode(), true);
30973107
return factory.createExprBlock(initIteratorTempVar, closeIfNotDone, valueTempVar.createReadNode());
30983108
}
30993109

0 commit comments

Comments
 (0)