Closed Bug 1954099 Opened 1 month ago Closed 22 days ago

Optimize RegExp instance/prototype guards

Categories

(Core :: JavaScript Engine, task, P1)

task

Tracking

()

RESOLVED FIXED
139 Branch
Tracking Status
firefox139 --- fixed

People

(Reporter: jandem, Assigned: jandem)

References

(Blocks 1 open bug)

Details

Attachments

(9 files)

48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review

We can now replace the following self-hosted functions with faster intrinsics that rely on fuses:

  • IsRegExpMethodOptimizable
  • IsRegExpSplitOptimizable
  • IsRegExpMatchAllOptimizable
  • IsRegExpStringIteratorNextOptimizable
  • IsStringMatchOptimizable
  • IsStringSearchOptimizable

The .exec property lookup in the RegExpExec intrinsic could use a similar fast path (as well as the CacheIR we generate for this intrinsic).

This will also make it easier to potentially port some of these functions to C++ in the future.

I've implemented this + a few related optimizations.

For the string-replace micro-benchmark below (best of 5 runs, all with --spectre-mitigations=off):

before: 371 ms
after:  346 ms
before --no-ion: 1596 ms
after --no-ion:  1334 ms

For a version with a string ("abc") instead of regular expression:

before: 223 ms
after:  210 ms
before --no-ion: 591 ms
after --no-ion:  470 ms

So we're faster especially without Ion (Baseline code being slower hurts us on Speedometer). The new fuse-based code is also simpler and will make it easier to rewrite builtins in C++ later if needed because it removes various property accesses.

function f() {
  var s = "foo";
  var re = /a|b/;
  var t = Date.now();
  for (var i = 0; i < 10_000_000; i++) {
    s = s.replace(re, "");
  }
  print(Date.now() - t);
  return s;
}
f();

This fuse is only checked from C++ code so it doesn't need to be an invalidating fuse.

This adds the fuse without using it yet.

The list of properties guarded by the fuse is based on js::RegExpPrototypeOptimizableRaw.
That optimization will be replaced with a fuse check in a later patch.

This is very similar to the global's boundFunctionShapeWithDefaultProto.

Later patches will use this shape to check for plain regular expression objects.

This replaces IsStringMatchOptimizable and similar self-hosted functions with
calls to either IsRegExpPrototypeOptimizable() or IsOptimizableRegExpObject(object).

On Speedometer 3 these intrinsics always return true.

The fuse also guards against changes to data properties so we no longer need
to check the property values explicitly in self-hosted code.

This makes IsRegExpPrototypeOptimizable() a no-op in Ion code as long as the
fuse isn't popped. The IsOptimizableRegExpObject(object) intrinsic is a
shape check in Ion code.

Add a fast path based on IsOptimizableRegExpObject to the C++ code, and also guard
on the fuse in the CacheIR code.

On Speedometer 3 this fast path is always used.

Self-hosted code used functions such as StringProtoHasNoMatch to check Symbol.match
and other symbols aren't defined on String.prototype or Object.prototype.

We can replace these functions with a fuse check. The fuse is popped when any of the
four symbols is added to String.prototype or Object.prototype or when
String.prototype's prototype is changed.

See Also: → 1956245
Severity: -- → N/A
Priority: -- → P1
See Also: → 1956420
Blocks: 1956420
See Also: 1956420
Pushed by jdemooij@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/6443bb48f276 part 1 - Fix OptimizePromiseLookupFuse base class. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/da7dd0b3756f part 2 - Add fuse for RegExp.prototype properties. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/06cc9671f128 part 3 - Store RegExpObject shape in the global object. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/2da432e568c7 part 4 - Replace RegExp checks in self-hosted code with new intrinsics based on the fuse. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/d687719a301c part 5 - Make RegExp.prototype fuse an invalidating fuse. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/d8e1c8893d80 part 6 - Add fast path to RegExpExec intrinsic. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/6640a9f186a8 part 7 - Optimize String.prototype symbol lookups with a fuse. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/4d4e7e868ec9 part 8 - Remove now unused ObjectHasPrototype intrinsic. r=mgaudet https://hg.mozilla.org/integration/autoland/rev/e815df607713 part 9 - Make StringPrototypeSymbols fuse an invalidating fuse. r=mgaudet

Either this bug or bug 1956420 lead to improvements:

7.2% on six-speed-regex-u-es5
7.8% on six-speed-regex-u-es6

Some improvement on Octane-regexp
Some improvement on Jetstream2-regexp

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: