[JSC] Implement `String#split` in C++
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/builtins/StringPrototype.js
Source/JavaScriptCore/runtime/RegExpObjectInlines.h
JSC는 ECMAScript builtin 최적화에 두 가지 계층을 갖추고 있습니다. 하나는 self-hosted JS builtin으로, 바이트코드로 컴파일된 뒤 범용 JIT로 처리됩니다. 다른 하나는 DFG intrinsic으로, 타입에 특화된 node를 통해 C++ 직접 호출 코드를 생성합니다. Watchpoint는 일종의 무효화 훅입니다. 최적화된 코드는 특정 불변 조건(예: RegExp.prototype[@@split]이 변경되지 않았음)을 가정한 뒤, 그 조건이 깨질 때 발동되는 watchpoint를 등록하게 됩니다.
이 commit은 String.prototype.split을 self-hosted JS builtin에서 C++ host function으로 이전하고, 새로운 StringSplit DFG node를 도입했습니다. 이 node는 separator가 문자열인 경우 operationStringSplit으로, primordial RegExp separator인 경우 operationStringSplitRegExp로 분기합니다. Fast path를 보호하는 watchpoint set은 세 가지입니다. m_stringSymbolSplitWatchpointSet, m_regExpSpeciesWatchpointSet, 그리고 regExpPrimordialPropertiesWatchpointSet에 추가된 splitSymbol입니다. 인스턴스별 override는 새로 도입된 isSymbolSplitFastAndNonObservable 내부의 structure check에서 탐지됩니다. 이 구조는 기존의 isSymbolReplaceFastAndNonObservable을 그대로 따른 것입니다.
String#split(sep) call
|
[DFG StringSplit node]
|
watchpoints valid?
yes / no
|
sep type check
|- String --> operationStringSplit
|- RegExp? --> isSymbolSplitFastAndNonObservable
|- yes --> operationStringSplitRegExp (C++ fast)
|- no --> RegExp.prototype[@@split] (JS, observable)
Significance
새로운 fast-path gate에 추가된 세 개의 watchpoint와 structure check는 String#replace C++ 마이그레이션 당시의 bug surface와 동일한 구조입니다. 이 형태에서 watchpoint race와 structure check bypass 유형의 버그가 반복적으로 발생했습니다. Split 집약적인 workload에서 11~23%의 성능 향상이 이 commit의 주목받는 성과이기는 합니다. 그러나 watchpoint로 보호되는 fast path가 추가될 때마다, gate의 불변 조건이 완전히 보존되지 않으면 관찰 가능한 side effect가 누출되는 새로운 window가 열립니다.