← All issues

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.

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.

🔒

Pattern-based audit directions for variant discovery

Subscribe to read more