[JSC] Add DFG / FTL intrinsic for `%TypedArray%.prototype.set`
Component: JSC JIT | a1e5cdc
%TypedArray%.prototype.set은 원래 C++ host function으로 실행됩니다. 이 함수는 receiver를 검증하고 offset을 변환한 뒤, source가 target 범위 안에 들어가는지 확인합니다. 이어서 영역이 겹치거나 type이 다른 source를 처리하고, 마지막으로 복사를 수행합니다. 한편 DFG와 FTL은 이런 builtin을 intrinsic으로 대체할 수 있습니다. Intrinsic은 일반적인 경우를 가정해 speculation을 수행하고, 가정이 틀리면 OSR exit로 빠져나옵니다. 이때 정확성은 clobberize(reads/writes), doesGC, 그리고 생성된 코드가 어떤 runtime 조건을 다시 검사하는지에 달려 있습니다. 문제는 typed array의 length가 변경 가능한 상태라는 점입니다. Buffer는 transfer로 detach될 수도 있고, resize될 수도 있습니다.
이번 patch로 TypedArraySet node가 추가되었습니다. 이 node는 먼저 두 array의 typed-array type이 같은지, 어느 쪽도 resizable이나 detached 상태가 아닌지, 복사 범위가 target 안에 들어가는지를 speculate합니다. 조건이 모두 맞으면 memmove만 수행하는 operation을 호출합니다. 반대로 speculation이 실패하면 OSR exit가 발생하고, 해당 호출 지점에서는 intrinsic이 비활성화됩니다. 변경 범위는 bytecode parser, abstract interpreter, clobberize, doesGC, fixup, safeToExecute, SpeculativeJIT, FTL lowering에 걸쳐 있습니다.
call site ─► TypedArraySet node
├─ check same TypedArrayType
├─ check !resizable, !detached
├─ check offset+len <= target len
├─ ok ─► operation: memmove only
└─ fail ─► OSR exit (intrinsic disabled at site)
Significance
작은 크기의 복사에서는 host call과 인자 검증 overhead가 비용의 대부분을 차지했습니다. 이번 변경으로 typed-array-set-small은 3.3배, typed-array-set-concat은 4.1배 개선되었습니다. Typed array storage로 raw memmove를 수행하기 위한 bounds, detach, resize 검사가 이제 compiler가 생성한 코드 안에서 이루어집니다.
Audit directions
- Bounds 연산.
compileTypedArraySet과 FTL lowering에서 offset 변환, offset+length overflow, operand fixup을 점검할 필요가 있습니다. - 임의의 JS 실행 이후의 검사. JS를 실행할 수 있는 모든 지점(offset의
valueOf, 그 밖의 side effect) 뒤에 detach/resize 검사가 오는지 확인해야 합니다. 또한 length와 base pointer를 stale 상태에서 가져오지 않고 다시 읽는지도 살펴봐야 합니다. Compiled code 실행 중 offset 변환 과정 안에서 resize가 발생하는 경우도 여기에 포함됩니다. - Effect 모델링.
clobberize와doesGC가 length와 vector의 read, array heap에 대한 write를 충분히 정밀하게 선언하는지 확인해야 합니다. 그래야 CSE와 LICM이 detach나 resize를 가로질러 node를 재배치하지 못합니다. - Type 및 alias 경계 조건. Uint8과 Uint8Clamped의 구분을 확인해야 합니다. SharedArrayBuffer aliasing도 점검 대상이며, 이 경우 memmove가 영역 중첩을 처리해야 합니다. 이 밖에 DFG/FTL 간 격차(
safeToExecute, abstract interpreter의 filtering,FTLCapabilities)와 operand 변환 이후의 OSR exit 복구도 살펴볼 필요가 있습니다. 기존-exit/-largestress test는 SharedArrayBuffer, growable buffer, subclass, typed array가 아닌 receiver를 사용한.call을 다루고 있습니다. 따라서 tier-up 이후 type이 다른 source를 넘기는 경우가 남아 있는 공백일 가능성이 더 높습니다.