[JSC] Inline string === "constant"
Component: JSC JIT | 8bd3c1b
JSTests/stress/string-strict-equal-short-constant-stale-8bit-substring.js
FTL은 JSC의 최상위 tier JIT입니다. DFG IR을 B3로 낮추고, 이를 다시 Air를 거쳐 machine code로 변환합니다.
한편 JSString은 rope(concatenation 또는 substring) 형태일 수도 있고, 이미 resolve된 StringImpl일 수도 있습니다.
이때 StringImpl은 8-bit(Latin-1) 또는 16-bit(UTF-16) 중 하나이며, JSString은 is8Bit flag를 별도로 캐시합니다.
문제는 string을 atomize하는 과정에서 StringImpl이 내용은 같지만 폭이 다른 atom으로 바뀔 수 있다는 점입니다.
이번 commit으로 FTL은 string === "constant" 비교를 inline 처리하게 되었습니다.
대상은 constant가 8-bit이고 길이가 16자 이하인 경우입니다.
먼저 length를 확인하고 string의 data를 로드한 뒤, 하나 또는 두 개의 slice를 BitOr(BitXor(slice0, c0), BitXor(slice1, c1)) 형태로 비교합니다.
여기에 ARM64용 B3 lowering rule이 새로 추가되어, 이 XOR/OR 형태를 cmp/ccmp chain으로 변환합니다.
Stress test는 keyword 형태의 비교, 8-bit 및 16-bit source, 그리고 위에 나온 stale 8-bit substring 사례를 다룹니다.
CompareStrictEq(str, K) (K 8-bit, <= 16 chars)
├─► not a cell / not string ─► slow path
├─► length != len(K) ─► false
├─► resolve + 8-bit check ─► slow path
└─► load slice(s): BitOr(BitXor(s0,k0), BitXor(s1,k1)) == 0
└─► ARM64 B3 lowering: cmp + ccmp chain
Significance
짧은 constant와의 비교는 parser, tokenizer, keyword 검사 코드에서 자주 등장합니다. 이번 inline 처리로 이런 hot path에서 범용 string 비교 호출이 사라집니다. 다만 새 ARM64 cmp/ccmp lowering은 string 전용이 아닙니다. 같은 XOR/OR 비교 형태를 생성하는 B3 코드라면 모두 이 경로를 타게 됩니다.
Audit directions
- Raw byte fast path에서의 표현 불일치. stale 8-bit test를 보면 이 invariant가 쉽게 깨질 수 있음을 알 수 있습니다. substring rope는 base의 8-bit flag를 캐시하는데, property key atomization 과정에서 16-bit impl로 바뀔 수 있기 때문입니다. 캐시된 flag나 length가 실제 storage와 어긋날 수 있는 경로를 모두 나열해 볼 필요가 있습니다. rope, substring, atomization, resolve 시점 등이 그 대상입니다. 이후 새 코드가 flag를 참조하는지, 실제 impl을 참조하는지 확인해야 합니다.
- 검사가 틀렸을 때의 read 범위. 16-bit data가 8-bit로 읽히는 경우를 가정할 수 있습니다. 이 경우 실제로 읽는 byte 수를 allocation 크기와 비교해 봐야 합니다. 특히 길이 9~16 구간에서 두 slice load가 서로 겹치는 부분이 중요합니다. 빈 string, 1~8, 9~16, 그리고 16/17 경계를 각각 시험해 볼 필요가 있습니다. 또한 fast path가 data를 로드하기 전에 resolve된
JSString인지 검사하는지도 확인해야 합니다. - 공유되는 B3 lowering rule. flag와 condition code 처리, 공유 leaf(
testXorOrCompareChainSharedLeaf32), immediate encoding, ZExt 및 32-bit/64-bit 폭 처리를 점검해야 합니다. 잘못 매칭되면 string과 무관한 B3 코드까지 miscompile될 가능성이 있습니다. 아울러 fast path가 fallback할 때 OSR exit와 Phi 상태가 올바르게 유지되는지도 확인이 필요합니다.