[JSC] New DFG node for StringIteratorPrototype.next
JSC의 DFG JIT는 알려진 host 함수 호출을 "intrinsic" 노드로 대체할 수 있는데, 이 노드는 inline으로 최적화된 기계어로 확장됩니다. StringIteratorPrototype.next()는 이전까지 JS builtin으로 호출 지점에서 inline 처리되던 함수였습니다. 이 commit에서는 이를 JSStringIteratorNextIntrinsic으로 태그된 C++ host 함수로 전환했습니다. 결과적으로 DFGByteCodeParser가 해당 호출 지점을 인식하고 custom node graph를 생성하는 경로가 마련되었습니다.
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
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
index는 exit 가능한 모든 노드 이후에 전진됩니다. 그러나 향후 patch에서 index 갱신 이후 지점에 OSR exit 가능한 새 노드가 삽입되면, 이 불변성이 조용히 깨질 수 있습니다. 확장된 노드 시퀀스 안에서 PutInternalField의 순서를 speculating 노드와의 관계를 기준으로 점검할 필요가 있습니다.
StringIteratorNextWithUndefined와 StringIteratorNext는 codegen을 공유하지만 done 값이 다릅니다. 두 노드를 혼동하는 type speculation 오류가 발생하면, undefined가 SpecString 타입으로 추론된 for-of 슬롯에 유입되어 하위에서 type confusion이 발생할 가능성이 있습니다.
새로 도입된 jsValueTupleResultWithoutUsingChildren()의 use-chain 처리가 잘못된 경우, GC가 살아있는 JSValue 슬롯을 dead로 판단할 가능성이 있습니다. iterator와 result 객체를 하나의 시퀀스에서 함께 sinking하는 조합은 alias analysis 관점에서 처음 등장하는 패턴으로, 별도 점검이 필요합니다. 또한 64비트 전용 분리로 인해, intrinsic 경로는 32비트 환경에서는 전혀 실행되지 않는다는 점도 고려해야 합니다.