← All issues

[JSC] Implement `%RegExpStringIteratorPrototype%.next` in C++

1452a43

JSC의 DFG와 FTL 계층은 자주 호출되는 builtin을 first-class IR 노드로 inline 처리하여, 컴파일러가 타입을 추론하고 JS 호출 오버헤드를 제거할 수 있도록 합니다. "primordial exec" guard는 watchpoint입니다. RegExp.prototype.exec가 여전히 원래의 native 구현으로 유지되고 있는 경우, fast path는 observable JS exec 호출을 우회하여 execInline을 직접 호출합니다. exec가 한 번이라도 교체되면, 호출 지점이 이미 상위 계층으로 올라간 뒤 반복 도중에 교체가 발생하더라도 watchpoint가 발동하고, JSC는 slow path로 OSR-exit해야 합니다.

JSTests/stress/regexp-string-iterator-next-adversarial.js

+ class Species extends RegExp {
+ static get [Symbol.species]() {
+ return function (p, f) {
+ matcher = new RegExp(p, f);
+ if (configure)
+ configure(matcher);
+ return matcher;
+ };
+ }
+ }

이 commit은 %RegExpStringIteratorPrototype%.next를 JavaScript builtin(현재 삭제됨)에서 native C++ 구현으로 전환합니다. 새로운 RegExpStringIteratorNext DFG 노드가 추가되었으며, DFG와 FTL 계층 모두에서 inline으로 컴파일됩니다. 반복 대상 RegExp에 primordial exec가 유지되는 경우, fast path를 통해 RegExpObject::execInline을 직접 호출합니다.

이제 모든 String.prototype.matchAll 루프는 watchpoint로 fast/slow 분기가 제어되는 새로운 JIT-inlined C++ hot path를 거칩니다. matchAll 벤치마크에서 최대 약 19%의 처리량 향상이 측정되었습니다.

새로운 노드에는 세 가지 정확성 위험 요소가 존재합니다. Watchpoint race / late deoptimization: fast path는 execInline으로 디스패치하기 전에 primordial-exec 상태를 확인합니다. guard와 결과 사용 사이에 exec 교체나 GC 이벤트로 watchpoint가 무효화될 경우, iterator가 잘못된 exec 구현의 결과를 반환할 가능성이 있습니다. adversarial 테스트는 반복 도중 exec 교체 시나리오를 다루지만, DFG/FTL 파이프라인에서의 실제 OSR-exit 타이밍은 제어하기가 더 어렵습니다. Inline result-object shape: fast path는 iteratorResultObjectStructure를 사용하여 {value, done} 객체를 inline으로 생성합니다. 호출자가 이 구조를 관찰하거나 수정할 수 있다면 해당 가정이 깨질 가능성이 있습니다. createIteratorResultObjectiteratorResultObjectStructureConcurrently를 점검해볼 필요가 있습니다. Unicode boundary advancement: advanceStringIndexRegExpPrototype.cpp에서 RegExpObjectInlines.h로 이동하여, 이제 execInline에서 직접 호출됩니다. 이 새로운 호출 지점에서 surrogate-pair boundary 관련 버그가 발생하면 off-by-one 이동이 생길 수 있습니다. 결과적으로 sticky/global Unicode regexp에서 무한 루프나 match 누락으로 이어질 가능성이 있습니다.