Commit ed5230a9 authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

improve exec performance on RegExp with named capture groups

When creating the results object for an exec call, V8 is calling into a
runtime helper once per named capture group. It can instead create that
object without any runtime calls, using existing CSA helpers. The only
additional cost of this approach is that it internalizes the capture
group names at regex compilation time, even if the script never calls
exec on that regex.

Bug: v8:6914

Change-Id: I58975b8d7aea05f8c67ae087608715099f62fc48
Reviewed-on: https://chromium-review.googlesource.com/c/1454268Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#59470}
parent f644fa40
......@@ -247,26 +247,26 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex);
GotoIf(WordEqual(maybe_names, SmiZero()), &out);
// One or more named captures exist, add a property for each one.
TNode<FixedArray> names = CAST(maybe_names);
TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrZero()));
// Allocate a new object to store the named capture properties.
// TODO(jgruber): Could be optimized by adding the object map to the heap
// root list.
TNode<IntPtrT> num_properties = WordSar(names_length, 1);
TNode<Context> native_context = LoadNativeContext(context);
TNode<Map> map = CAST(LoadContextElement(
native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
TNode<NameDictionary> properties =
AllocateNameDictionary(NameDictionary::kInitialCapacity);
TNode<NameDictionary> properties = AllocateNameDictionary(num_properties);
TNode<JSObject> group_object =
CAST(AllocateJSObjectFromMap(map, properties));
StoreObjectField(result, JSRegExpResult::kGroupsOffset, group_object);
// One or more named captures exist, add a property for each one.
TNode<FixedArray> names = CAST(maybe_names);
TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrZero()));
TVARIABLE(IntPtrT, var_i, IntPtrZero());
Variable* vars[] = {&var_i};
......@@ -285,15 +285,32 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TNode<HeapObject> capture =
CAST(LoadFixedArrayElement(result_elements, SmiUntag(index)));
// TODO(jgruber): Calling into runtime to create each property is slow.
// Either we should create properties entirely in CSA (should be doable),
// or only call runtime once and loop there.
CallRuntime(Runtime::kCreateDataProperty, context, group_object, name,
capture);
// TODO(v8:8213): For maintainability, we should call a CSA/Torque
// implementation of CreateDataProperty instead.
// At this point the spec says to call CreateDataProperty. However, we can
// skip most of the steps and go straight to adding a dictionary entry
// because we know a bunch of useful facts:
// - All keys are non-numeric internalized strings
// - No keys repeat
// - Receiver has no prototype
// - Receiver isn't used as a prototype
// - Receiver isn't any special object like a Promise intrinsic object
// - Receiver is extensible
// - Receiver has no interceptors
Label add_dictionary_property_slow(this, Label::kDeferred);
Add<NameDictionary>(properties, name, capture,
&add_dictionary_property_slow);
var_i = i_plus_2;
Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out,
&loop);
BIND(&add_dictionary_property_slow);
// If the dictionary needs resizing, the above Add call will jump here
// before making any changes. This shouldn't happen because we allocated
// the dictionary with enough space above.
Unreachable();
}
}
......
......@@ -990,8 +990,12 @@ Handle<FixedArray> RegExpParser::CreateCaptureNameMap() {
for (int i = 0; i < named_captures_->length(); i++) {
RegExpCapture* capture = named_captures_->at(i);
MaybeHandle<String> name = factory->NewStringFromTwoByte(capture->name());
array->set(i * 2, *name.ToHandleChecked());
Vector<const uc16> capture_name(capture->name()->data(),
capture->name()->size());
// CSA code in ConstructNewResultFromMatchInfo requires these strings to be
// internalized so they can be used as property names in the 'exec' results.
Handle<String> name = factory->InternalizeTwoByteString(capture_name);
array->set(i * 2, *name);
array->set(i * 2 + 1, Smi::FromInt(capture->index()));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment