Commit 10f7b7ec authored by jgruber's avatar jgruber Committed by Commit bot

[regexp] Add slow exec stub to reduce code size

CSA builtins can become very large, and the RegExp builtins are currently the
main offender (e.g. @@match's code size is over 50k). This is due to the fact
that most RegExp builtins rely on RegExpBuiltinExec (fairly large itself),
which is then inlined multiple times in many builtins.

This CL reduces the snapshot size for an x64 release build by 80k by turning
slow-path RegExpBuiltinExec calls into stub calls (i.e. removing code
duplication through inlining) and completely removing the code path for fast
RegExp instances in RegExpExec (it is never taken).

BUG=v8:5339,v8:5737

Review-Url: https://codereview.chromium.org/2745053003
Cr-Commit-Position: refs/heads/master@{#43889}
parent 162553a1
...@@ -448,10 +448,12 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, ...@@ -448,10 +448,12 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow); Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow);
Bind(&stack_overflow); Bind(&stack_overflow);
TailCallRuntime(Runtime::kThrowStackOverflow, context); CallRuntime(Runtime::kThrowStackOverflow, context);
Unreachable();
Bind(&rethrow); Bind(&rethrow);
TailCallRuntime(Runtime::kRegExpExecReThrow, context); CallRuntime(Runtime::kRegExpExecReThrow, context);
Unreachable();
} }
Bind(&runtime); Bind(&runtime);
...@@ -726,6 +728,17 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, ...@@ -726,6 +728,17 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map,
if_ismodified); if_ismodified);
} }
// Slow path stub for RegExpPrototypeExec to decrease code size.
TF_BUILTIN(RegExpPrototypeExecSlow, RegExpBuiltinsAssembler) {
typedef RegExpPrototypeExecSlowDescriptor Descriptor;
Node* const regexp = Parameter(Descriptor::kReceiver);
Node* const string = Parameter(Descriptor::kString);
Node* const context = Parameter(Descriptor::kContext);
Return(RegExpPrototypeExecBody(context, regexp, string, false));
}
// ES#sec-regexp.prototype.exec // ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string ) // RegExp.prototype.exec ( string )
TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
...@@ -754,8 +767,8 @@ TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { ...@@ -754,8 +767,8 @@ TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
Bind(&if_isslowpath); Bind(&if_isslowpath);
{ {
Node* const result = Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
RegExpPrototypeExecBody(context, receiver, string, false); context, receiver, string);
Return(result); Return(result);
} }
} }
...@@ -1330,64 +1343,49 @@ TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { ...@@ -1330,64 +1343,49 @@ TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
Node* string) { Node* string) {
Isolate* isolate = this->isolate(); CSA_ASSERT(this, Word32BinaryNot(IsFastRegExpMap(context, LoadMap(regexp))));
Node* const null = NullConstant();
Variable var_result(this, MachineRepresentation::kTagged); Variable var_result(this, MachineRepresentation::kTagged);
Label out(this), if_isfastpath(this), if_isslowpath(this); Label out(this);
Node* const map = LoadMap(regexp);
BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath);
Bind(&if_isfastpath);
{
Node* const result = RegExpPrototypeExecBody(context, regexp, string, true);
var_result.Bind(result);
Goto(&out);
}
Bind(&if_isslowpath); // Take the slow path of fetching the exec property, calling it, and
{ // verifying its return value.
// Take the slow path of fetching the exec property, calling it, and
// verifying its return value.
// Get the exec property. // Get the exec property.
Node* const exec = Node* const exec =
GetProperty(context, regexp, isolate->factory()->exec_string()); GetProperty(context, regexp, isolate()->factory()->exec_string());
// Is {exec} callable? // Is {exec} callable?
Label if_iscallable(this), if_isnotcallable(this); Label if_iscallable(this), if_isnotcallable(this);
GotoIf(TaggedIsSmi(exec), &if_isnotcallable); GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
Node* const exec_map = LoadMap(exec); Node* const exec_map = LoadMap(exec);
Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
Bind(&if_iscallable); Bind(&if_iscallable);
{ {
Callable call_callable = CodeFactory::Call(isolate); Callable call_callable = CodeFactory::Call(isolate());
Node* const result = CallJS(call_callable, context, exec, regexp, string); Node* const result = CallJS(call_callable, context, exec, regexp, string);
var_result.Bind(result); var_result.Bind(result);
GotoIf(WordEqual(result, null), &out); GotoIf(WordEqual(result, NullConstant()), &out);
ThrowIfNotJSReceiver(context, result, ThrowIfNotJSReceiver(context, result,
MessageTemplate::kInvalidRegExpExecResult, "unused"); MessageTemplate::kInvalidRegExpExecResult, "unused");
Goto(&out); Goto(&out);
} }
Bind(&if_isnotcallable); Bind(&if_isnotcallable);
{ {
ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
"RegExp.prototype.exec"); "RegExp.prototype.exec");
Node* const result = Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
RegExpPrototypeExecBody(context, regexp, string, false); context, regexp, string);
var_result.Bind(result); var_result.Bind(result);
Goto(&out); Goto(&out);
}
} }
Bind(&out); Bind(&out);
......
...@@ -698,6 +698,8 @@ class Isolate; ...@@ -698,6 +698,8 @@ class Isolate;
CPP(ReflectSetPrototypeOf) \ CPP(ReflectSetPrototypeOf) \
\ \
/* RegExp */ \ /* RegExp */ \
TFS(RegExpPrototypeExecSlow, BUILTIN, kNoExtraICState, \
RegExpPrototypeExecSlow, 1) \
CPP(RegExpCapture1Getter) \ CPP(RegExpCapture1Getter) \
CPP(RegExpCapture2Getter) \ CPP(RegExpCapture2Getter) \
CPP(RegExpCapture3Getter) \ CPP(RegExpCapture3Getter) \
......
...@@ -50,6 +50,7 @@ class PlatformInterfaceDescriptor; ...@@ -50,6 +50,7 @@ class PlatformInterfaceDescriptor;
V(ConstructStub) \ V(ConstructStub) \
V(ConstructTrampoline) \ V(ConstructTrampoline) \
V(RegExpExec) \ V(RegExpExec) \
V(RegExpPrototypeExecSlow) \
V(RegExpReplace) \ V(RegExpReplace) \
V(RegExpSplit) \ V(RegExpSplit) \
V(CopyFastSmiOrObjectElements) \ V(CopyFastSmiOrObjectElements) \
...@@ -658,6 +659,13 @@ class RegExpExecDescriptor : public CallInterfaceDescriptor { ...@@ -658,6 +659,13 @@ class RegExpExecDescriptor : public CallInterfaceDescriptor {
static const Register CodeRegister(); static const Register CodeRegister();
}; };
class RegExpPrototypeExecSlowDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kReceiver, kString)
DECLARE_DEFAULT_DESCRIPTOR(RegExpPrototypeExecSlowDescriptor,
CallInterfaceDescriptor, kParameterCount)
};
class RegExpReplaceDescriptor : public CallInterfaceDescriptor { class RegExpReplaceDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kReceiver, kString, kReplaceValue) DEFINE_PARAMETERS(kReceiver, kString, kReplaceValue)
......
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