JSC RegExp#lastIndex Inline Cache
d2c63be
Source/JavaScriptCore/bytecode/InlineCacheCompiler.cpp
+ case AccessCase::RegExpLastIndexLoad: {
+ ASSERT(!accessCase.viaGlobalProxy());
+ fallThrough.append(jit.branchIfNotType(baseGPR, RegExpObjectType));
+ jit.loadValue(CCallHelpers::Address(baseGPR, RegExpObject::offsetOfLastIndex()), valueRegs);
+ succeed();
+ return;
+ }
+
+ case AccessCase::RegExpLastIndexStore: {
+ ASSERT(!accessCase.viaGlobalProxy());
+ fallThrough.append(jit.branchIfNotType(baseGPR, RegExpObjectType));
+ fallThrough.append(
+ jit.branchTestPtr(
+ CCallHelpers::NonZero,
+ CCallHelpers::Address(baseGPR, RegExpObject::offsetOfRegExpAndFlags()),
+ CCallHelpers::TrustedImm32(RegExpObject::lastIndexIsNotWritableFlag)));
+ jit.storeValue(valueRegs, CCallHelpers::Address(baseGPR, RegExpObject::offsetOfLastIndex()));
+ succeed();
+ return;
+ }
JSC's inline cache system caches property operations as type-specialized machine-code stubs. When the JIT encounters a property access, it builds a stub keyed on the object's Structure so that subsequent accesses hit the stub directly, bypassing the generic slow path. This commit extends the IC to handle reg.lastIndex reads and writes directly. RegExp#lastIndex is accessed on every iteration of global/sticky regex loops, making this a high-frequency property.
The load IC is a direct field access — branch on RegExpObjectType, then load from the known offset. The store IC adds a writable-flag check: RegExp#lastIndex is own and non-configurable, but its writable attribute can be cleared (e.g., via Object.freeze()), so the IC must fall through to the slow path for frozen RegExps. Benchmarks show ~2x speedup on the existing regexp-last-index benchmark and ~1.3x on a dedicated microbenchmark.
Significance
This is a meaningful throughput improvement for regex-heavy code, and new IC cases in JSC's inline cache compiler expand the JIT attack surface. The polymorphic IC path must also handle plain objects that happen to have a lastIndex property, not just RegExp instances — the branchIfNotType(baseGPR, RegExpObjectType) guard handles this by falling through to the generic slow path for non-RegExp objects.
Audit directions
Aaa Aaaaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaa Aaaaaaaaaaaa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaaa Aa Aaaaa a Aaaa Aaaaa Aaaa Aaaaaa Aaaaaaaaaaa Aaaaaaa Aaaaaaa Aaaa Aaaaaaaa Aaaaa Aaa Aaaaa Aa Aaaa Aaaaa Aaaaaaa Aaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaaaaaaaaa a Aaaaaa Aaaa Aaa Aaaaa Aaaaaaa Aa Aaa Aaaaaaaaaaa Aaaa Aa Aaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaa Aaaaaa Aaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaaa Aaa Aaaaa Aa Aaaaaaaaaaa Aaa Aaa Aaaa Aaaaaa Aaaa Aaaaaaaaaaa Aa Aaaaa Aaaaaaaa a Aa Aaa Aa Aaaaaaaaaa Aa Aaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaaa Aaaaa Aaaaa Aaaaaaaaaa Aaaa Aaaaaaaaaa Aaaaaaaaa