From 1b4d64f7119c424fbc658fbb8aa6790a81e29e28 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Thu, 28 Jan 2016 18:55:24 +0900 Subject: [PATCH] Bug 887016 - Part 7: Add RegExpInstanceOptimizable. r=nbp --- js/src/builtin/RegExp.cpp | 52 +++++++++++++++++++++ js/src/builtin/RegExp.h | 6 +++ js/src/jit/CodeGenerator.cpp | 71 +++++++++++++++++++++++++++++ js/src/jit/CodeGenerator.h | 3 ++ js/src/jit/InlinableNatives.h | 1 + js/src/jit/IonBuilder.h | 1 + js/src/jit/Lowering.cpp | 12 +++++ js/src/jit/Lowering.h | 1 + js/src/jit/MCallOptimize.cpp | 31 +++++++++++++ js/src/jit/MIR.h | 29 ++++++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/shared/LIR-shared.h | 25 ++++++++++ js/src/jit/shared/LOpcodes-shared.h | 1 + js/src/vm/RegExpObject.cpp | 9 +++- js/src/vm/RegExpObject.h | 25 ++++++++++ js/src/vm/SelfHosting.cpp | 2 + 16 files changed, 269 insertions(+), 1 deletion(-) diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 7c1706fbea32..30303887811c 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -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(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; +} diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h index 6e6f0e12885d..4fbed9ebafeb 100644 --- a/js/src/builtin/RegExp.h +++ b/js/src/builtin/RegExp.h @@ -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); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index c40fa4b71a46..5ed9acf82f46 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2035,6 +2035,77 @@ CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototype masm.jump(ool->rejoin()); } +class OutOfLineRegExpInstanceOptimizable : public OutOfLineCodeBase +{ + 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(RegExpReplace); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 109695e76791..eb31e8b550b5 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -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); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index d72798eb71a6..b493179b214d 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -69,6 +69,7 @@ _(RegExpTester) \ _(IsRegExpObject) \ _(RegExpPrototypeOptimizable) \ + _(RegExpInstanceOptimizable) \ \ _(String) \ _(StringSplit) \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 1cac86fa9b79..eab60f58fdce 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -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); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 7cc66c00e1fc..f9e9889e870b 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -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) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 05a9ac8185cf..b6aaa185b27b 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -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); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 10a4d3181ada..9f3f56d7c56c 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -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) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index bb8db7abb09c..1cee04598f74 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8017,6 +8017,35 @@ class MRegExpPrototypeOptimizable } }; +class MRegExpInstanceOptimizable + : public MBinaryInstruction, + public MixPolicy, 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 MStrReplace : public MTernaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 6d5210260a49..1dbb8e30fb4b 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -148,6 +148,7 @@ namespace jit { _(RegExpMatcher) \ _(RegExpTester) \ _(RegExpPrototypeOptimizable) \ + _(RegExpInstanceOptimizable) \ _(RegExpReplace) \ _(StringReplace) \ _(Lambda) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index cd4dfeb5ffd2..8fb3603a22d2 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -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: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index b31f7edca496..0f6d615de891 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -209,6 +209,7 @@ _(RegExpMatcher) \ _(RegExpTester) \ _(RegExpPrototypeOptimizable) \ + _(RegExpInstanceOptimizable) \ _(RegExpReplace) \ _(StringReplace) \ _(Substr) \ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index ddf8d95df92a..cbd4ea0cea50 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -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 diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index e2546dcf4ae8..925fa22636ad 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -336,6 +336,13 @@ class RegExpCompartment */ ReadBarriered optimizableRegExpPrototypeShape_; + /* + * The shape of RegExp instance that satisfies following: + * * lastProperty is lastIndex + * * prototype is RegExp.prototype + */ + ReadBarriered 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) { diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index e36b121d5815..b57275acb2e8 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -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),