[JSC] New DFG node for StringIteratorPrototype.next
93518ed
JSC의 DFG JIT는 알려진 host 함수 호출을 "intrinsic" 노드로 대체할 수 있는데, 이 노드는 inline으로 최적화된 기계어로 확장됩니다. StringIteratorPrototype.next()는 이전까지 JS builtin으로 호출 지점에서 inline 처리되던 함수였습니다. 이 commit에서는 이를 JSStringIteratorNextIntrinsic으로 태그된 C++ host 함수로 전환했습니다. 결과적으로 DFGByteCodeParser가 해당 호출 지점을 인식하고 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);
+ ...
+ // OSR exit 가능한 모든 노드 이후에 iterator를 전진시킴
+ addToGraph(PutInternalField, OpInfo(static_cast<uint32_t>(JSStringIterator::Field::Index)), iterator, nextPosition);
+ setResult(resultObject);
+ return CallOptimizationResult::Inlined;
+ }
DFG는 이 호출을 직렬 노드 시퀀스로 확장합니다. 먼저 CheckStructure guard로 타입을 확인하고, GetInternalField를 두 차례 실행하여 내부 필드를 조회합니다. 이어서 새 StringIteratorNextWithUndefined 노드가 (char|undefined, nextIndex) tuple을 생성하고, NewObject/PutByOffset으로 결과 객체를 구성한 다음, PutInternalField가 index를 전진시키는 순서로 확장됩니다. 이 PutInternalField는 OSR exit 가능한 모든 노드보다 뒤에 생성되므로, deopt가 이미 전진된 index로 next()를 재실행하는 상황이 차단됩니다. 단, 이 경로는 64비트 환경에서만 동작합니다. StringIteratorNextWithUndefined는 기존 StringIteratorNext(for-of에서 사용)의 형제 노드로, 소진 시점에 빈 문자열 sentinel 대신 undefined를 반환한다는 점이 유일한 차이입니다. 이로써 for-of 경로에서의 SpecString 타입 추론이 유지됩니다.
Significance
babel로 트랜스파일된 코드에서 흔히 볼 수 있는 iterator.next() 직접 호출이, 이제는 interpret되는 builtin 대신 최적화된 JIT fast path로 컴파일됩니다. 벤치마크 기준으로 약 1.7배 빠른 성능을 보였습니다(105ms → 61ms). 또한 이번 변경으로 boxed JSValue 요소를 포함한 최초의 DFG tuple 노드가 도입되었습니다. 이 인프라는 향후 다른 intrinsic을 구현할 때 재사용될 것입니다.
Audit directions
Aaaaaa Aaaa Aaa Aa Aa Aaa Aaaaaa Aaa Aa Aaaaaaa Aaaaa Aa Aa Aaa Aaa Aaaa Aaa a Aaa Aaaaa a Aaaa Aaa Aa a Aaaaa Aaa Aa Aaa Aaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaa Aaaa Aaa Aaaa Aaa Aaa Aaaaa
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaa Aaaa Aa Aaaaa a Aaa Aaaa Aaaa Aaaaaaaaaaa Aaa Aaaaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaa Aaa Aaaaaa Aaa Aaaa Aaaa Aaaa Aaaaaaaaaa Aaa Aaaa Aaaaa
Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaa Aaa Aaa Aaaa Aaaaaaaaa Aaa Aaaaa Aaa Aaaa Aaaaa Aaaaaaaaa Aaaaaa Aaa Aaa Aaaaa Aa Aaaaaaaaa Aaa Aaaaa Aaaaaaaa Aaaa Aa Aaaa Aaaaa Aa Aaa Aaaaaa Aa Aaaa Aa Aaa Aaa Aaaaaaaaa Aaa Aaaa Aaaaa Aa Aaaa Aaaa Aa Aaaa Aaaa
🔒New JIT-expanded intrinsic with OSR exit ordering constraints and novel tuple node infrastructure — several audit directions included.
더 확인하려면 구독해 주세요