← All reports

[JSC] Inline string === "constant"

Component: JSC JIT | 8bd3c1b

JSTests/stress/string-strict-equal-short-constant-stale-8bit-substring.js

+// substring rope는 base의 8-bit flag를 캐시하지만, base를 atomize하면 동일한 내용의 16-bit StringImpl로 바뀔 수 있습니다.
+function test(s) { return s === "abcde"; }
+noInline(test);
+...
+const sub = content.slice(0, 5);
+
+const wide16 = ["", ("\u3042" + content).replace("\u3042", "")].join("");
+const object = {};
+object[wide16] = 1;
+object[content] = 2;
+
+for (let i = 0; i < testLoopCount; ++i) {
+ if (test(sub) !== true)
+ throw new Error("substring of a base swapped to a 16-bit atom compared unequal");
+}

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

짧은 constant와의 비교는 parser, tokenizer, keyword 검사 코드에서 자주 등장합니다. 이번 inline 처리로 이런 hot path에서 범용 string 비교 호출이 사라집니다. 다만 새 ARM64 cmp/ccmp lowering은 string 전용이 아닙니다. 같은 XOR/OR 비교 형태를 생성하는 B3 코드라면 모두 이 경로를 타게 됩니다.