← All issues

[JSC] New DFG node for StringIteratorPrototype.next

93518ed

JSC's DFG JIT can replace known host-function calls with "intrinsic" nodes that expand inline to optimized machine code. StringIteratorPrototype.next() was previously a JS builtin inlined at the call site; this commit moves it to a C++ host function tagged JSStringIteratorNextIntrinsic so DFGByteCodeParser can recognize the callsite and emit a custom node graph.

Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

+ case JSStringIteratorNextIntrinsic: {
+ if (!is64Bit())
+ return CallOptimizationResult::DidNothing;
+
+ Node* iterator = get(virtualRegisterForArgumentIncludingThis(0, registerOffset));
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(globalObject->stringIteratorStructure())), iterator);
+
+ Node* position = addToGraph(GetInternalField, OpInfo(static_cast<uint32_t>(JSStringIterator::Field::Index)), OpInfo(SpecInt32Only), iterator);
+ Node* string = addToGraph(GetInternalField, OpInfo(static_cast<uint32_t>(JSStringIterator::Field::IteratedString)), OpInfo(SpecString), iterator);
+
+ Node* tuple = addToGraph(StringIteratorNextWithUndefined, Edge(string), Edge(position));
+ Node* value = addToGraph(ExtractFromTuple, OpInfo(0), tuple);
+ value->setResult(NodeResultJS);
+ Node* nextPosition = addToGraph(ExtractFromTuple, OpInfo(1), tuple);
+ nextPosition->setResult(NodeResultInt32);
+ ...
+ // Advance the iterator only after all nodes that can OSR exit
+ addToGraph(PutInternalField, OpInfo(static_cast<uint32_t>(JSStringIterator::Field::Index)), iterator, nextPosition);
+ setResult(resultObject);
+ return CallOptimizationResult::Inlined;
+ }

The DFG expands the call into straight-line nodes: a CheckStructure guard, two GetInternalField reads, a new StringIteratorNextWithUndefined node producing a (char|undefined, nextIndex) tuple, NewObject/PutByOffset for the result object, and finally a PutInternalField to advance the index — emitted after all OSR-exit-capable nodes so a deopt cannot replay next() with an already-advanced index. The path is 64-bit only. StringIteratorNextWithUndefined is a sibling of the existing StringIteratorNext (used in for-of), differing only in returning undefined instead of an empty-string sentinel at exhaustion, preserving SpecString typing in the for-of path.

Manual iterator.next() calls — prevalent in babel-transpiled code — now compile to a tight JIT fast path instead of an interpreted builtin, around 1.7x faster (105ms to 61ms in the benchmark). The change also introduces the first DFG tuple node with a boxed JSValue element, infrastructure future intrinsics will reuse.

🔒

New JIT-expanded intrinsic with OSR exit ordering constraints and novel tuple node infrastructure — several audit directions included.

Subscribe to read more