Commit db99bdff authored by jgruber's avatar jgruber Committed by Commit bot

[regexp] Port RegExp.prototype.exec to TurboFan

This ports RegExp.prototype.exec to a TurboFan builtin.

LastMatchInfo is now stored on the context in order to be able to access
it from the stub.

Unmodified RegExp instances go through a fast path of accessing the
lastIndex property as an in-object field, while modified instances call
into runtime for lastIndex loads and stores.

Octane/regexp shows slight improvements (between 0 and 5%) with this CL.

BUG=v8:5339

Review-Url: https://codereview.chromium.org/2375953002
Cr-Commit-Position: refs/heads/master@{#39899}
parent 96c25e58
......@@ -932,6 +932,7 @@ v8_source_set("v8_base") {
"src/builtins/builtins-object.cc",
"src/builtins/builtins-proxy.cc",
"src/builtins/builtins-reflect.cc",
"src/builtins/builtins-regexp.cc",
"src/builtins/builtins-sharedarraybuffer.cc",
"src/builtins/builtins-string.cc",
"src/builtins/builtins-symbol.cc",
......
......@@ -1637,9 +1637,11 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{ // -- R e g E x p
// Builtin functions for RegExp.prototype.
Handle<JSFunction> regexp_fun = InstallFunction(
global, "RegExp", JS_REGEXP_TYPE, JSRegExp::kSize,
isolate->initial_object_prototype(), Builtins::kIllegal);
Handle<JSObject> prototype =
factory->NewJSObject(isolate->object_function(), TENURED);
Handle<JSFunction> regexp_fun =
InstallFunction(global, "RegExp", JS_REGEXP_TYPE, JSRegExp::kSize,
prototype, Builtins::kIllegal);
InstallWithIntrinsicDefaultProto(isolate, regexp_fun,
Context::REGEXP_FUNCTION_INDEX);
regexp_fun->shared()->SetConstructStub(
......@@ -1665,6 +1667,15 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
initial_map->set_unused_property_fields(0);
initial_map->set_instance_size(initial_map->instance_size() +
num_fields * kPointerSize);
// RegExp.prototype setup.
// Install the "constructor" property on the {prototype}.
JSObject::AddProperty(prototype, factory->constructor_string(), regexp_fun,
DONT_ENUM);
SimpleInstallFunction(prototype, "exec", Builtins::kRegExpPrototypeExec, 1,
true, DONT_ENUM);
}
{ // -- E r r o r
......
This diff is collapsed.
......@@ -537,6 +537,9 @@ namespace internal {
TFJ(AtomicsLoad, 3) \
TFJ(AtomicsStore, 4) \
\
/* RegExp */ \
TFJ(RegExpPrototypeExec, 2) \
\
/* String */ \
ASM(StringConstructor) \
ASM(StringConstructor_ConstructStub) \
......
......@@ -2229,6 +2229,34 @@ Node* CodeStubAssembler::ToThisValue(Node* context, Node* value,
return var_value.value();
}
Node* CodeStubAssembler::ThrowIfNotInstanceType(Node* context, Node* value,
InstanceType instance_type,
char const* method_name) {
Label out(this), throw_exception(this, Label::kDeferred);
Variable var_value_map(this, MachineRepresentation::kTagged);
GotoIf(WordIsSmi(value), &throw_exception);
// Load the instance type of the {value}.
var_value_map.Bind(LoadMap(value));
Node* const value_instance_type = LoadMapInstanceType(var_value_map.value());
Branch(Word32Equal(value_instance_type, Int32Constant(instance_type)), &out,
&throw_exception);
// The {value} is not a compatible receiver for this method.
Bind(&throw_exception);
CallRuntime(
Runtime::kThrowIncompatibleMethodReceiver, context,
HeapConstant(factory()->NewStringFromAsciiChecked(method_name, TENURED)),
value);
var_value_map.Bind(UndefinedConstant());
Goto(&out); // Never reached.
Bind(&out);
return var_value_map.value();
}
Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index) {
// Translate the {index} into a Word.
index = SmiToWord(index);
......
......@@ -467,6 +467,13 @@ class CodeStubAssembler : public compiler::CodeAssembler {
PrimitiveType primitive_type,
char const* method_name);
// Throws a TypeError for {method_name} if {value} is not of the given
// instance type. Returns {value}'s map.
compiler::Node* ThrowIfNotInstanceType(compiler::Node* context,
compiler::Node* value,
InstanceType instance_type,
char const* method_name);
// String helpers.
// Load a character from a String (might flatten a ConsString).
compiler::Node* StringCharCodeAt(compiler::Node* string,
......
......@@ -465,6 +465,14 @@ Node* CodeAssembler::CallStub(Callable const& callable, Node* context,
result_size);
}
Node* CodeAssembler::CallStub(Callable const& callable, Node* context,
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
size_t result_size) {
Node* target = HeapConstant(callable.code());
return CallStub(callable.descriptor(), target, context, arg1, arg2, arg3,
arg4, result_size);
}
Node* CodeAssembler::CallStubN(Callable const& callable, Node** args,
size_t result_size) {
Node* target = HeapConstant(callable.code());
......
......@@ -348,6 +348,8 @@ class CodeAssembler {
Node* arg2, size_t result_size = 1);
Node* CallStub(Callable const& callable, Node* context, Node* arg1,
Node* arg2, Node* arg3, size_t result_size = 1);
Node* CallStub(Callable const& callable, Node* context, Node* arg1,
Node* arg2, Node* arg3, Node* arg4, size_t result_size = 1);
Node* CallStubN(Callable const& callable, Node** args,
size_t result_size = 1);
......
......@@ -96,6 +96,7 @@ enum ContextLookupFlags {
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
V(REGEXP_LAST_MATCH_INFO_INDEX, JSObject, regexp_last_match_info) \
V(REJECT_PROMISE_NO_DEBUG_EVENT_INDEX, JSFunction, \
reject_promise_no_debug_event) \
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
......
......@@ -14,11 +14,12 @@
var GlobalArray = global.Array;
var GlobalObject = global.Object;
var GlobalRegExp = global.RegExp;
var GlobalRegExpPrototype;
var GlobalRegExpPrototype = GlobalRegExp.prototype;
var InternalArray = utils.InternalArray;
var InternalPackedArray = utils.InternalPackedArray;
var MaxSimple;
var MinSimple;
var RegExpExecJS = GlobalRegExp.prototype.exec;
var matchSymbol = utils.ImportNow("match_symbol");
var replaceSymbol = utils.ImportNow("replace_symbol");
var searchSymbol = utils.ImportNow("search_symbol");
......@@ -163,55 +164,6 @@ macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING)
endmacro
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
function RegExpExecJS(string) {
if (!IS_REGEXP(this)) {
throw %make_type_error(kIncompatibleMethodReceiver,
'RegExp.prototype.exec', this);
}
string = TO_STRING(string);
var lastIndex;
var global = TO_BOOLEAN(REGEXP_GLOBAL(this));
var sticky = TO_BOOLEAN(REGEXP_STICKY(this));
var updateLastIndex = global || sticky;
if (updateLastIndex) {
// TODO(jgruber): This is actually ToLength in the spec, but we bailout
// to the runtime in %_RegExpExec if lastIndex is not a Smi, so we are
// smart here and trick both TurboFan and Crankshaft to produce a Smi.
// This is a terrible hack, and correct for subtle reasons; it's a clear
// indicator that we need a predictable RegExp implementation where we
// don't need to add specific work-arounds for certain compiler issues.
lastIndex = +this.lastIndex;
if (lastIndex > string.length) {
this.lastIndex = 0;
return null;
} else if (lastIndex <= 0) {
lastIndex = 0;
}
lastIndex = lastIndex|0;
} else {
lastIndex = 0;
}
// matchIndices is either null or the RegExpLastMatchInfo array.
var matchIndices = %_RegExpExec(this, string, lastIndex, RegExpLastMatchInfo);
if (IS_NULL(matchIndices)) {
if (updateLastIndex) this.lastIndex = 0;
return null;
}
// Successful match.
if (updateLastIndex) {
this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
}
RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
}
%FunctionRemovePrototype(RegExpExecJS);
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
// Also takes an optional exec method in case our caller
......@@ -1022,16 +974,11 @@ function RegExpSpecies() {
// -------------------------------------------------------------------
%FunctionSetInstanceClassName(GlobalRegExp, 'RegExp');
GlobalRegExpPrototype = new GlobalObject();
%FunctionSetPrototype(GlobalRegExp, GlobalRegExpPrototype);
%AddNamedProperty(
GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM);
%SetCode(GlobalRegExp, RegExpConstructor);
utils.InstallGetter(GlobalRegExp, speciesSymbol, RegExpSpecies);
utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
"exec", RegExpExecJS,
"test", RegExpSubclassTest,
"toString", RegExpToString,
"compile", RegExpCompileJS,
......@@ -1103,6 +1050,8 @@ for (var i = 1; i < 10; ++i) {
}
%ToFastProperties(GlobalRegExp);
%InstallToContext(["regexp_last_match_info", RegExpLastMatchInfo]);
// -------------------------------------------------------------------
// Internal
......
......@@ -502,6 +502,7 @@
'builtins/builtins-object.cc',
'builtins/builtins-proxy.cc',
'builtins/builtins-reflect.cc',
'builtins/builtins-regexp.cc',
'builtins/builtins-sharedarraybuffer.cc',
'builtins/builtins-string.cc',
'builtins/builtins-symbol.cc',
......
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