зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1260435 - Make RegExp hoistable when it's used only in RegExpBuiltinExec. r=h4writer
This commit is contained in:
Родитель
03effee7b4
Коммит
200bc0e463
|
@ -809,7 +809,7 @@ function RegExpBuiltinExec(R, S, forTest) {
|
|||
// This check is here for RegExpTest. RegExp_prototype_Exec does same
|
||||
// thing already.
|
||||
if (!IsRegExpObject(R))
|
||||
return callFunction(CallRegExpMethodIfWrapped, R, R, S, forTest, "RegExpBuiltinExec");
|
||||
return UnwrapAndCallRegExpBuiltinExec(R, S, forTest);
|
||||
|
||||
// Steps 1-2 (skipped).
|
||||
|
||||
|
@ -863,6 +863,14 @@ function RegExpBuiltinExec(R, S, forTest) {
|
|||
return result;
|
||||
}
|
||||
|
||||
function UnwrapAndCallRegExpBuiltinExec(R, S, forTest) {
|
||||
return callFunction(CallRegExpMethodIfWrapped, R, S, forTest, "CallRegExpBuiltinExec");
|
||||
}
|
||||
|
||||
function CallRegExpBuiltinExec(S, forTest) {
|
||||
return RegExpBuiltinExec(this, S, forTest);
|
||||
}
|
||||
|
||||
// ES6 21.2.5.13.
|
||||
function RegExpTest(string) {
|
||||
// Steps 1-2.
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "jit/LIR.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsopcodeinlines.h"
|
||||
|
@ -1827,9 +1829,207 @@ jit::ApplyTypeInformation(MIRGenerator* mir, MIRGraph& graph)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check if `def` is only the N-th operand of `useDef`.
|
||||
static inline size_t
|
||||
IsExclusiveNthOperand(MDefinition* useDef, size_t n, MDefinition* def)
|
||||
{
|
||||
uint32_t num = useDef->numOperands();
|
||||
if (n >= num || useDef->getOperand(n) != def)
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
if (i == n)
|
||||
continue;
|
||||
if (useDef->getOperand(i) == def)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t
|
||||
IsExclusiveThisArg(MCall* call, MDefinition* def)
|
||||
{
|
||||
return IsExclusiveNthOperand(call, MCall::IndexOfThis(), def);
|
||||
}
|
||||
|
||||
static size_t
|
||||
IsExclusiveFirstArg(MCall* call, MDefinition* def)
|
||||
{
|
||||
return IsExclusiveNthOperand(call, MCall::IndexOfArgument(0), def);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsRegExpHoistableCall(MCall* call, MDefinition* def)
|
||||
{
|
||||
if (call->isConstructing())
|
||||
return false;
|
||||
|
||||
JSAtom* name;
|
||||
JSFunction* fun = call->getSingleTarget();
|
||||
if (fun) {
|
||||
if (!fun->isSelfHostedBuiltin())
|
||||
return false;
|
||||
name = GetSelfHostedFunctionName(fun);
|
||||
} else {
|
||||
MDefinition* funDef = call->getFunction();
|
||||
if (funDef->isDebugCheckSelfHosted())
|
||||
funDef = funDef->toDebugCheckSelfHosted()->input();
|
||||
if (funDef->isTypeBarrier())
|
||||
funDef = funDef->toTypeBarrier()->input();
|
||||
|
||||
if (!funDef->isCallGetIntrinsicValue())
|
||||
return false;
|
||||
name = funDef->toCallGetIntrinsicValue()->name();
|
||||
}
|
||||
|
||||
// Hoistable only if the RegExp is the first argument of RegExpBuiltinExec.
|
||||
CompileRuntime* runtime = GetJitContext()->runtime;
|
||||
if (name == runtime->names().RegExpBuiltinExec ||
|
||||
name == runtime->names().UnwrapAndCallRegExpBuiltinExec ||
|
||||
name == runtime->names().RegExpMatcher ||
|
||||
name == runtime->names().RegExpTester ||
|
||||
name == runtime->names().RegExpSearcher)
|
||||
{
|
||||
return IsExclusiveFirstArg(call, def);
|
||||
}
|
||||
|
||||
if (name == runtime->names().RegExp_prototype_Exec)
|
||||
return IsExclusiveThisArg(call, def);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanCompareWithoutToPrimitive(MCompare* compare, MDefinition* def)
|
||||
{
|
||||
JSOp op = compare->jsop();
|
||||
// Strict equality comparison won't invoke @@toPrimitive.
|
||||
if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE)
|
||||
return true;
|
||||
|
||||
if (op != JSOP_EQ && op != JSOP_NE) {
|
||||
// Relational comparison always invoke @@toPrimitive.
|
||||
MOZ_ASSERT(op == JSOP_GT || op == JSOP_GE || op == JSOP_LT || op == JSOP_LE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loose equality comparison can invoke @@toPrimitive.
|
||||
MDefinition* value;
|
||||
if (compare->lhs() == def) {
|
||||
value = compare->rhs();
|
||||
} else {
|
||||
MOZ_ASSERT(compare->rhs() == def);
|
||||
value = compare->lhs();
|
||||
}
|
||||
|
||||
if (value->mightBeType(MIRType::Boolean) || value->mightBeType(MIRType::String) ||
|
||||
value->mightBeType(MIRType::Int32) ||
|
||||
value->mightBeType(MIRType::Double) || value->mightBeType(MIRType::Float32) ||
|
||||
value->mightBeType(MIRType::Symbol))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetNotInWorklist(MDefinitionVector& worklist)
|
||||
{
|
||||
for (size_t i = 0; i < worklist.length(); i++)
|
||||
worklist[i]->setNotInWorklist();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsRegExpHoistable(MDefinition* regexp, MDefinitionVector& worklist, bool* hoistable)
|
||||
{
|
||||
MOZ_ASSERT(worklist.length() == 0);
|
||||
|
||||
if (!worklist.append(regexp))
|
||||
return false;
|
||||
regexp->setInWorklist();
|
||||
|
||||
for (size_t i = 0; i < worklist.length(); i++) {
|
||||
MDefinition* def = worklist[i];
|
||||
for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
|
||||
// Ignore resume points. At this point all uses are listed.
|
||||
// No DCE or GVN or something has happened.
|
||||
if (use->consumer()->isResumePoint())
|
||||
continue;
|
||||
|
||||
MDefinition* useDef = use->consumer()->toDefinition();
|
||||
|
||||
// Step through a few white-listed ops.
|
||||
if (useDef->isPhi() || useDef->isFilterTypeSet() || useDef->isGuardShape()) {
|
||||
if (useDef->isInWorklist())
|
||||
continue;
|
||||
|
||||
if (!worklist.append(useDef))
|
||||
return false;
|
||||
useDef->setInWorklist();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Instructions that doesn't invoke unknown code that may modify
|
||||
// RegExp instance or pass it to elsewhere.
|
||||
if (useDef->isRegExpMatcher() || useDef->isRegExpTester() ||
|
||||
useDef->isRegExpSearcher())
|
||||
{
|
||||
if (IsExclusiveNthOperand(useDef, 0, def))
|
||||
continue;
|
||||
} else if (useDef->isLoadFixedSlot() || useDef->isTypeOf()) {
|
||||
continue;
|
||||
} else if (useDef->isCompare()) {
|
||||
if (CanCompareWithoutToPrimitive(useDef->toCompare(), def))
|
||||
continue;
|
||||
}
|
||||
// Instructions that modifies `lastIndex` property.
|
||||
else if (useDef->isStoreFixedSlot()) {
|
||||
if (IsExclusiveNthOperand(useDef, 0, def)) {
|
||||
MStoreFixedSlot* store = useDef->toStoreFixedSlot();
|
||||
if (store->slot() == RegExpObject::lastIndexSlot())
|
||||
continue;
|
||||
}
|
||||
} else if (useDef->isSetPropertyCache()) {
|
||||
if (IsExclusiveNthOperand(useDef, 0, def)) {
|
||||
MSetPropertyCache* setProp = useDef->toSetPropertyCache();
|
||||
if (setProp->idval()->isConstant()) {
|
||||
Value propIdVal = setProp->idval()->toConstant()->toJSValue();
|
||||
if (propIdVal.isString()) {
|
||||
CompileRuntime* runtime = GetJitContext()->runtime;
|
||||
if (propIdVal.toString() == runtime->names().lastIndex)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// MCall is safe only for some known safe functions.
|
||||
else if (useDef->isCall()) {
|
||||
if (IsRegExpHoistableCall(useDef->toCall(), def))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Everything else is unsafe.
|
||||
SetNotInWorklist(worklist);
|
||||
worklist.clear();
|
||||
*hoistable = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
SetNotInWorklist(worklist);
|
||||
worklist.clear();
|
||||
*hoistable = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::MakeMRegExpHoistable(MIRGraph& graph)
|
||||
{
|
||||
MDefinitionVector worklist(graph.alloc());
|
||||
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
|
||||
for (MDefinitionIterator iter(*block); iter; iter++) {
|
||||
if (!*iter)
|
||||
|
@ -1840,24 +2040,9 @@ jit::MakeMRegExpHoistable(MIRGraph& graph)
|
|||
|
||||
MRegExp* regexp = iter->toRegExp();
|
||||
|
||||
// Test if MRegExp is hoistable by looking at all uses.
|
||||
bool hoistable = true;
|
||||
for (MUseIterator i = regexp->usesBegin(); i != regexp->usesEnd(); i++) {
|
||||
// Ignore resume points. At this point all uses are listed.
|
||||
// No DCE or GVN or something has happened.
|
||||
if (i->consumer()->isResumePoint())
|
||||
continue;
|
||||
|
||||
MOZ_ASSERT(i->consumer()->isDefinition());
|
||||
|
||||
// All MRegExp* MIR's don't adjust the regexp.
|
||||
MDefinition* use = i->consumer()->toDefinition();
|
||||
if (use->isRegExpMatcher() || use->isRegExpTester() || use->isRegExpSearcher())
|
||||
continue;
|
||||
|
||||
hoistable = false;
|
||||
break;
|
||||
}
|
||||
bool hoistable = false;
|
||||
if (!IsRegExpHoistable(regexp, worklist, &hoistable))
|
||||
return false;
|
||||
|
||||
if (!hoistable)
|
||||
continue;
|
||||
|
@ -1865,8 +2050,9 @@ jit::MakeMRegExpHoistable(MIRGraph& graph)
|
|||
// Make MRegExp hoistable
|
||||
regexp->setMovable();
|
||||
|
||||
// That would be incorrect for global/sticky, because lastIndex could be wrong.
|
||||
// Therefore setting the lastIndex to 0. That is faster than a not movable regexp.
|
||||
// That would be incorrect for global/sticky, because lastIndex
|
||||
// could be wrong. Therefore setting the lastIndex to 0. That is
|
||||
// faster than a not movable regexp.
|
||||
RegExpObject* source = regexp->source();
|
||||
if (source->sticky() || source->global()) {
|
||||
MOZ_ASSERT(regexp->mustClone());
|
||||
|
|
|
@ -299,6 +299,12 @@
|
|||
macro(wasm, wasm, "wasm") \
|
||||
macro(watch, watch, "watch") \
|
||||
macro(WeakSet_add, WeakSet_add, "WeakSet_add") \
|
||||
macro(RegExp_prototype_Exec, RegExp_prototype_Exec, "RegExp_prototype_Exec") \
|
||||
macro(UnwrapAndCallRegExpBuiltinExec, UnwrapAndCallRegExpBuiltinExec, "UnwrapAndCallRegExpBuiltinExec") \
|
||||
macro(RegExpBuiltinExec, RegExpBuiltinExec, "RegExpBuiltinExec") \
|
||||
macro(RegExpMatcher, RegExpMatcher, "RegExpMatcher") \
|
||||
macro(RegExpSearcher, RegExpSearcher, "RegExpSearcher") \
|
||||
macro(RegExpTester, RegExpTester, "RegExpTester") \
|
||||
macro(weekday, weekday, "weekday") \
|
||||
macro(writable, writable, "writable") \
|
||||
macro(year, year, "year") \
|
||||
|
|
|
@ -3069,8 +3069,13 @@ js::SelfHostedFunction(JSContext* cx, HandlePropertyName propName)
|
|||
bool
|
||||
js::IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name)
|
||||
{
|
||||
return fun->isSelfHostedBuiltin() &&
|
||||
fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString() == name;
|
||||
return fun->isSelfHostedBuiltin() && GetSelfHostedFunctionName(fun) == name;
|
||||
}
|
||||
|
||||
JSAtom*
|
||||
js::GetSelfHostedFunctionName(JSFunction* fun)
|
||||
{
|
||||
return &fun->getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom();
|
||||
}
|
||||
|
||||
static_assert(JSString::MAX_LENGTH <= INT32_MAX,
|
||||
|
|
|
@ -23,6 +23,9 @@ namespace js {
|
|||
bool
|
||||
IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name);
|
||||
|
||||
JSAtom*
|
||||
GetSelfHostedFunctionName(JSFunction* fun);
|
||||
|
||||
bool
|
||||
IsCallSelfHostedNonGenericMethod(NativeImpl impl);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче