Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Unified Diff: src/builtins/builtins-regexp-gen.cc

Issue 2797993002: [regexp] Ensure there are no shape changes on the fast path (Closed)
Patch Set: Add test Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/builtins/builtins-regexp-gen.h ('k') | src/builtins/builtins-string-gen.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/builtins/builtins-regexp-gen.cc
diff --git a/src/builtins/builtins-regexp-gen.cc b/src/builtins/builtins-regexp-gen.cc
index b76ee8cec7b39e78592459f93a3702bae43dd6db..d87fdf600d9f23978f1eea03b0a4fa5ba3d329d0 100644
--- a/src/builtins/builtins-regexp-gen.cc
+++ b/src/builtins/builtins-regexp-gen.cc
@@ -482,7 +482,9 @@ Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
Node* const int_zero = IntPtrConstant(0);
Node* const smi_zero = SmiConstant(Smi::kZero);
- if (!is_fastpath) {
+ if (is_fastpath) {
+ CSA_ASSERT(this, IsFastRegExpNoPrototype(context, regexp, LoadMap(regexp)));
+ } else {
ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
"RegExp.prototype.exec");
}
@@ -499,18 +501,23 @@ Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath);
var_lastindex.Bind(regexp_lastindex);
- // Omit ToLength if lastindex is a non-negative smi.
- Label call_tolength(this, Label::kDeferred), next(this);
- Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
+ if (is_fastpath) {
+ // ToLength on a positive smi is a nop and can be skipped.
+ CSA_ASSERT(this, TaggedIsPositiveSmi(regexp_lastindex));
+ } else {
+ // Omit ToLength if lastindex is a non-negative smi.
+ Label call_tolength(this, Label::kDeferred), next(this);
+ Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
- Bind(&call_tolength);
- {
- var_lastindex.Bind(
- CallBuiltin(Builtins::kToLength, context, regexp_lastindex));
- Goto(&next);
- }
+ Bind(&call_tolength);
+ {
+ var_lastindex.Bind(
+ CallBuiltin(Builtins::kToLength, context, regexp_lastindex));
+ Goto(&next);
+ }
- Bind(&next);
+ Bind(&next);
+ }
}
// Check whether the regexp is global or sticky, which determines whether we
@@ -658,7 +665,12 @@ Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver(
return var_value_map.value();
}
-Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) {
+Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(Node* const context,
+ Node* const object,
+ Node* const map) {
+ Label out(this);
+ Variable var_result(this, MachineRepresentation::kWord32);
+
Node* const native_context = LoadNativeContext(context);
Node* const regexp_fun =
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
@@ -666,17 +678,33 @@ Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) {
LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const has_initialmap = WordEqual(map, initial_map);
- return has_initialmap;
+ var_result.Bind(has_initialmap);
+ GotoIfNot(has_initialmap, &out);
+
+ // The smi check is required to omit ToLength(lastIndex) calls with possible
+ // user-code execution on the fast path.
+ Node* const last_index = FastLoadLastIndex(object);
+ var_result.Bind(TaggedIsPositiveSmi(last_index));
+ Goto(&out);
+
+ Bind(&out);
+ return var_result.value();
}
// RegExp fast path implementations rely on unmodified JSRegExp instances.
// We use a fairly coarse granularity for this and simply check whether both
-// the regexp itself is unmodified (i.e. its map has not changed) and its
-// prototype is unmodified.
+// the regexp itself is unmodified (i.e. its map has not changed), its
+// prototype is unmodified, and lastIndex is a non-negative smi.
void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
+ Node* const object,
Node* const map,
Label* const if_isunmodified,
Label* const if_ismodified) {
+ CSA_ASSERT(this, WordEqual(LoadMap(object), map));
+
+ // TODO(ishell): Update this check once map changes for constant field
+ // tracking are landing.
+
Node* const native_context = LoadNativeContext(context);
Node* const regexp_fun =
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
@@ -692,18 +720,21 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
Node* const proto_has_initialmap =
WordEqual(proto_map, initial_proto_initial_map);
- // TODO(ishell): Update this check once map changes for constant field
- // tracking are landing.
+ GotoIfNot(proto_has_initialmap, if_ismodified);
- Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
+ // The smi check is required to omit ToLength(lastIndex) calls with possible
+ // user-code execution on the fast path.
+ Node* const last_index = FastLoadLastIndex(object);
+ Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
}
-Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
- Node* const map) {
+Node* RegExpBuiltinsAssembler::IsFastRegExp(Node* const context,
+ Node* const object,
+ Node* const map) {
Label yup(this), nope(this), out(this);
Variable var_result(this, MachineRepresentation::kWord32);
- BranchIfFastRegExp(context, map, &yup, &nope);
+ BranchIfFastRegExp(context, object, map, &yup, &nope);
Bind(&yup);
var_result.Bind(Int32Constant(1));
@@ -753,7 +784,7 @@ TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
Node* const string = ToString(context, maybe_string);
Label if_isfastpath(this), if_isslowpath(this);
- Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath,
+ Branch(IsFastRegExpNoPrototype(context, receiver, regexp_map), &if_isfastpath,
&if_isslowpath);
Bind(&if_isfastpath);
@@ -960,7 +991,8 @@ TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
Node* const receiver = maybe_receiver;
Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
- Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath);
+ Branch(IsFastRegExpNoPrototype(context, receiver, map), &if_isfastpath,
+ &if_isslowpath);
Bind(&if_isfastpath);
Return(FlagsGetter(context, receiver, true));
@@ -1405,8 +1437,6 @@ TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
Node* string) {
- CSA_ASSERT(this, Word32BinaryNot(IsFastRegExpMap(context, LoadMap(regexp))));
-
Variable var_result(this, MachineRepresentation::kTagged);
Label out(this);
@@ -1471,7 +1501,7 @@ TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
Node* const string = ToString(context, maybe_string);
Label fast_path(this), slow_path(this);
- BranchIfFastRegExp(context, map, &fast_path, &slow_path);
+ BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path);
Bind(&fast_path);
{
@@ -1800,12 +1830,24 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
Node* const match_length = LoadStringLength(match);
GotoIfNot(SmiEqual(match_length, smi_zero), &loop);
- Node* const last_index =
- CallBuiltin(Builtins::kToLength, context,
- LoadLastIndex(context, regexp, is_fastpath));
+ Node* last_index = LoadLastIndex(context, regexp, is_fastpath);
+ if (is_fastpath) {
+ CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
+ } else {
+ last_index = CallBuiltin(Builtins::kToLength, context, last_index);
+ }
+
Node* const new_last_index =
AdvanceStringIndex(string, last_index, is_unicode);
+ if (is_fastpath) {
+ // On the fast path, we can be certain that lastIndex can never be
+ // incremented to overflow the Smi range since the maximal string
+ // length is less than the maximal Smi value.
+ STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
+ CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index));
+ }
+
StoreLastIndex(context, regexp, new_last_index, is_fastpath);
Goto(&loop);
@@ -1839,7 +1881,7 @@ TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
Node* const string = ToString(context, maybe_string);
Label fast_path(this), slow_path(this);
- BranchIfFastRegExp(context, map, &fast_path, &slow_path);
+ BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path);
Bind(&fast_path);
RegExpPrototypeMatchBody(context, receiver, string, true);
@@ -1961,7 +2003,7 @@ TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
Node* const string = ToString(context, maybe_string);
Label fast_path(this), slow_path(this);
- BranchIfFastRegExp(context, map, &fast_path, &slow_path);
+ BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path);
Bind(&fast_path);
RegExpPrototypeSearchBodyFast(context, receiver, string);
@@ -2219,7 +2261,7 @@ TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
Node* const maybe_limit = Parameter(Descriptor::kLimit);
Node* const context = Parameter(Descriptor::kContext);
- CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
+ CSA_ASSERT(this, IsFastRegExp(context, regexp, LoadMap(regexp)));
CSA_ASSERT(this, IsString(string));
// TODO(jgruber): Even if map checks send us to the fast path, we still need
@@ -2273,7 +2315,7 @@ TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
Node* const string = ToString(context, maybe_string);
Label stub(this), runtime(this, Label::kDeferred);
- BranchIfFastRegExp(context, map, &stub, &runtime);
+ BranchIfFastRegExp(context, receiver, map, &stub, &runtime);
Bind(&stub);
Return(CallBuiltin(Builtins::kRegExpSplit, context, receiver, string,
@@ -2599,7 +2641,7 @@ TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
Node* const replace_value = Parameter(Descriptor::kReplaceValue);
Node* const context = Parameter(Descriptor::kContext);
- CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
+ CSA_ASSERT(this, IsFastRegExp(context, regexp, LoadMap(regexp)));
CSA_ASSERT(this, IsString(string));
Label checkreplacestring(this), if_iscallable(this),
@@ -2688,7 +2730,7 @@ TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
Label stub(this), runtime(this, Label::kDeferred);
- BranchIfFastRegExp(context, map, &stub, &runtime);
+ BranchIfFastRegExp(context, receiver, map, &stub, &runtime);
Bind(&stub);
Return(CallBuiltin(Builtins::kRegExpReplace, context, receiver, string,
« no previous file with comments | « src/builtins/builtins-regexp-gen.h ('k') | src/builtins/builtins-string-gen.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698