Bug 887016 - Part 7: Add RegExpInstanceOptimizable. r=nbp

This commit is contained in:
Tooru Fujisawa 2016-01-28 18:55:24 +09:00
parent e866ceb7f2
commit 1b4d64f711
16 changed files with 269 additions and 1 deletions

View File

@ -1104,3 +1104,55 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* resul
*result = true;
return true;
}
bool
js::RegExpInstanceOptimizable(JSContext* cx, unsigned argc, Value* vp)
{
// This can only be called from self-hosted code.
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
uint8_t result = false;
if (!RegExpInstanceOptimizableRaw(cx, &args[0].toObject(), &args[1].toObject(), &result))
return false;
args.rval().setBoolean(result);
return true;
}
bool
js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* rx, JSObject* proto, uint8_t* result)
{
JS::AutoCheckCannotGC nogc;
if (!rx->isNative()) {
*result = false;
return true;
}
NativeObject* nobj = static_cast<NativeObject*>(rx);
Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
if (shape == nobj->lastProperty()) {
*result = true;
return true;
}
if (rx->hasLazyPrototype()) {
*result = false;
return true;
}
if (rx->getTaggedProto().toObjectOrNull() != proto) {
*result = false;
return true;
}
if (!RegExpObject::isInitialShape(nobj)) {
*result = false;
return true;
}
cx->compartment()->regExps.setOptimizableRegExpInstanceShape(nobj->lastProperty());
*result = true;
return true;
}

View File

@ -97,6 +97,12 @@ RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp);
extern bool
RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* result);
extern bool
RegExpInstanceOptimizable(JSContext* cx, unsigned argc, Value* vp);
extern bool
RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* rx, JSObject* proto, uint8_t* result);
// RegExp ClassSpec members used in RegExpObject.cpp.
extern bool
regexp_construct(JSContext* cx, unsigned argc, Value* vp);

View File

@ -2035,6 +2035,77 @@ CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototype
masm.jump(ool->rejoin());
}
class OutOfLineRegExpInstanceOptimizable : public OutOfLineCodeBase<CodeGenerator>
{
LRegExpInstanceOptimizable* ins_;
public:
explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
: ins_(ins)
{ }
void accept(CodeGenerator* codegen) {
codegen->visitOutOfLineRegExpInstanceOptimizable(this);
}
LRegExpInstanceOptimizable* ins() const {
return ins_;
}
};
void
CodeGenerator::visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
{
Register object = ToRegister(ins->object());
Register output = ToRegister(ins->output());
Register temp = ToRegister(ins->temp());
OutOfLineRegExpInstanceOptimizable* ool = new(alloc()) OutOfLineRegExpInstanceOptimizable(ins);
addOutOfLineCode(ool, ins->mir());
masm.loadJSContext(temp);
masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
masm.loadPtr(Address(temp, JSCompartment::offsetOfRegExps()), temp);
masm.loadPtr(Address(temp, RegExpCompartment::offsetOfOptimizableRegExpInstanceShape()),
temp);
masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
masm.move32(Imm32(0x1), output);
masm.bind(ool->rejoin());
}
void
CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOptimizable* ool)
{
LRegExpInstanceOptimizable* ins = ool->ins();
Register object = ToRegister(ins->object());
Register proto = ToRegister(ins->proto());
Register output = ToRegister(ins->output());
Register temp = ToRegister(ins->temp());
saveVolatile(output);
masm.reserveStack(sizeof(void*));
masm.moveStackPtrTo(temp);
masm.setupUnalignedABICall(output);
masm.loadJSContext(output);
masm.passABIArg(output);
masm.passABIArg(object);
masm.passABIArg(proto);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, RegExpInstanceOptimizableRaw));
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
masm.load8ZeroExtend(Address(masm.getStackPointer(), 0), output);
masm.freeStack(sizeof(void*));
restoreVolatile(output);
masm.jump(ool->rejoin());
}
typedef JSString* (*RegExpReplaceFn)(JSContext*, HandleString, HandleObject, HandleString);
static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);

View File

@ -49,6 +49,7 @@ class OutOfLineIsConstructor;
class OutOfLineRegExpMatcher;
class OutOfLineRegExpTester;
class OutOfLineRegExpPrototypeOptimizable;
class OutOfLineRegExpInstanceOptimizable;
class OutOfLineLambdaArrow;
class CodeGenerator : public CodeGeneratorSpecific
@ -116,6 +117,8 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
void visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* lir);
void visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototypeOptimizable* ool);
void visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* lir);
void visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOptimizable* ool);
void visitRegExpReplace(LRegExpReplace* lir);
void visitStringReplace(LStringReplace* lir);
void emitSharedStub(ICStub::Kind kind, LInstruction* lir);

View File

@ -69,6 +69,7 @@
_(RegExpTester) \
_(IsRegExpObject) \
_(RegExpPrototypeOptimizable) \
_(RegExpInstanceOptimizable) \
\
_(String) \
_(StringSplit) \

View File

@ -824,6 +824,7 @@ class IonBuilder
InliningStatus inlineRegExpTester(CallInfo& callInfo);
InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
InliningStatus inlineRegExpPrototypeOptimizable(CallInfo& callInfo);
InliningStatus inlineRegExpInstanceOptimizable(CallInfo& callInfo);
// Object natives and intrinsics.
InliningStatus inlineObjectCreate(CallInfo& callInfo);

View File

@ -2295,6 +2295,18 @@ LIRGenerator::visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins)
define(lir, ins);
}
void
LIRGenerator::visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins)
{
MOZ_ASSERT(ins->object()->type() == MIRType_Object);
MOZ_ASSERT(ins->proto()->type() == MIRType_Object);
MOZ_ASSERT(ins->type() == MIRType_Boolean);
LRegExpInstanceOptimizable* lir = new(alloc()) LRegExpInstanceOptimizable(useRegister(ins->object()),
useRegister(ins->proto()),
temp());
define(lir, ins);
}
void
LIRGenerator::visitRegExpReplace(MRegExpReplace* ins)
{

View File

@ -166,6 +166,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitRegExpMatcher(MRegExpMatcher* ins);
void visitRegExpTester(MRegExpTester* ins);
void visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins);
void visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins);
void visitRegExpReplace(MRegExpReplace* ins);
void visitStringReplace(MStringReplace* ins);
void visitBinarySharedStub(MBinarySharedStub* ins);

View File

@ -184,6 +184,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineIsRegExpObject(callInfo);
case InlinableNative::RegExpPrototypeOptimizable:
return inlineRegExpPrototypeOptimizable(callInfo);
case InlinableNative::RegExpInstanceOptimizable:
return inlineRegExpInstanceOptimizable(callInfo);
// String natives.
case InlinableNative::String:
@ -1909,6 +1911,35 @@ IonBuilder::inlineRegExpPrototypeOptimizable(CallInfo& callInfo)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineRegExpInstanceOptimizable(CallInfo& callInfo)
{
if (callInfo.argc() != 2 || callInfo.constructing()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
return InliningStatus_NotInlined;
}
MDefinition* rxArg = callInfo.getArg(0);
MDefinition* protoArg = callInfo.getArg(1);
if (rxArg->type() != MIRType_Object)
return InliningStatus_NotInlined;
if (protoArg->type() != MIRType_Object)
return InliningStatus_NotInlined;
if (getInlineReturnType() != MIRType_Boolean)
return InliningStatus_NotInlined;
callInfo.setImplicitlyUsedUnchecked();
MInstruction* opt = MRegExpInstanceOptimizable::New(alloc(), rxArg, protoArg);
current->add(opt);
current->push(opt);
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineStrReplace(CallInfo& callInfo)
{

View File

@ -8017,6 +8017,35 @@ class MRegExpPrototypeOptimizable
}
};
class MRegExpInstanceOptimizable
: public MBinaryInstruction,
public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
{
explicit MRegExpInstanceOptimizable(MDefinition* object, MDefinition* proto)
: MBinaryInstruction(object, proto)
{
setResultType(MIRType_Boolean);
setMovable();
}
public:
INSTRUCTION_HEADER(RegExpInstanceOptimizable)
static MRegExpInstanceOptimizable* New(TempAllocator& alloc, MDefinition* obj,
MDefinition* proto) {
return new(alloc) MRegExpInstanceOptimizable(obj, proto);
}
MDefinition* object() const {
return getOperand(0);
}
MDefinition* proto() const {
return getOperand(1);
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
template <class Policy1>
class MStrReplace
: public MTernaryInstruction,

View File

@ -148,6 +148,7 @@ namespace jit {
_(RegExpMatcher) \
_(RegExpTester) \
_(RegExpPrototypeOptimizable) \
_(RegExpInstanceOptimizable) \
_(RegExpReplace) \
_(StringReplace) \
_(Lambda) \

View File

@ -4353,6 +4353,31 @@ class LRegExpPrototypeOptimizable : public LInstructionHelper<1, 1, 1>
}
};
class LRegExpInstanceOptimizable : public LInstructionHelper<1, 2, 1>
{
public:
LIR_HEADER(RegExpInstanceOptimizable);
explicit LRegExpInstanceOptimizable(const LAllocation& object, const LAllocation& proto,
const LDefinition& temp) {
setOperand(0, object);
setOperand(1, proto);
setTemp(0, temp);
}
const LAllocation* object() {
return getOperand(0);
}
const LAllocation* proto() {
return getOperand(1);
}
const LDefinition* temp() {
return getTemp(0);
}
MRegExpInstanceOptimizable* mir() const {
return mir_->toRegExpInstanceOptimizable();
}
};
class LStrReplace : public LCallInstructionHelper<1, 3, 0>
{
public:

View File

@ -209,6 +209,7 @@
_(RegExpMatcher) \
_(RegExpTester) \
_(RegExpPrototypeOptimizable) \
_(RegExpInstanceOptimizable) \
_(RegExpReplace) \
_(StringReplace) \
_(Substr) \

View File

@ -721,7 +721,8 @@ RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
RegExpCompartment::RegExpCompartment(JSRuntime* rt)
: set_(rt),
matchResultTemplateObject_(nullptr),
optimizableRegExpPrototypeShape_(nullptr)
optimizableRegExpPrototypeShape_(nullptr),
optimizableRegExpInstanceShape_(nullptr)
{}
RegExpCompartment::~RegExpCompartment()
@ -852,6 +853,12 @@ RegExpCompartment::sweep(JSRuntime* rt)
{
optimizableRegExpPrototypeShape_.set(nullptr);
}
if (optimizableRegExpInstanceShape_ &&
IsAboutToBeFinalized(&optimizableRegExpInstanceShape_))
{
optimizableRegExpInstanceShape_.set(nullptr);
}
}
bool

View File

@ -336,6 +336,13 @@ class RegExpCompartment
*/
ReadBarriered<Shape*> optimizableRegExpPrototypeShape_;
/*
* The shape of RegExp instance that satisfies following:
* * lastProperty is lastIndex
* * prototype is RegExp.prototype
*/
ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
public:
@ -365,10 +372,19 @@ class RegExpCompartment
void setOptimizableRegExpPrototypeShape(Shape* shape) {
optimizableRegExpPrototypeShape_ = shape;
}
Shape* getOptimizableRegExpInstanceShape() {
return optimizableRegExpInstanceShape_;
}
void setOptimizableRegExpInstanceShape(Shape* shape) {
optimizableRegExpInstanceShape_ = shape;
}
static size_t offsetOfOptimizableRegExpPrototypeShape() {
return offsetof(RegExpCompartment, optimizableRegExpPrototypeShape_);
}
static size_t offsetOfOptimizableRegExpInstanceShape() {
return offsetof(RegExpCompartment, optimizableRegExpInstanceShape_);
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
};
@ -409,6 +425,15 @@ class RegExpObject : public NativeObject
static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
static bool isInitialShape(NativeObject* nobj) {
Shape* shape = nobj->lastProperty();
if (!shape->hasSlot())
return false;
if (shape->maybeSlot() != LAST_INDEX_SLOT)
return false;
return true;
}
const Value& getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
void setLastIndex(double d) {

View File

@ -2373,6 +2373,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("RegExpCreate", intrinsic_RegExpCreate, 2,0),
JS_INLINABLE_FN("RegExpPrototypeOptimizable", RegExpPrototypeOptimizable, 1,0,
RegExpPrototypeOptimizable),
JS_INLINABLE_FN("RegExpInstanceOptimizable", RegExpInstanceOptimizable, 1,0,
RegExpInstanceOptimizable),
// See builtin/RegExp.h for descriptions of the regexp_* functions.
JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),