diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index e0c04c75556d..d7bfc87dc063 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2213,6 +2213,63 @@ CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOp masm.jump(ool->rejoin()); } +static void +FindFirstDollarIndex(MacroAssembler& masm, Register str, Register len, Register chars, + Register temp, Register output, bool isLatin1) +{ + masm.loadStringChars(str, chars); + + masm.move32(Imm32(0), output); + + Label start, done; + masm.bind(&start); + if (isLatin1) + masm.load8ZeroExtend(BaseIndex(chars, output, TimesOne), temp); + else + masm.load16ZeroExtend(BaseIndex(chars, output, TimesTwo), temp); + + masm.branch32(Assembler::Equal, temp, Imm32('$'), &done); + + masm.add32(Imm32(1), output); + masm.branch32(Assembler::NotEqual, output, len, &start); + + masm.move32(Imm32(-1), output); + + masm.bind(&done); +} + +typedef bool (*GetFirstDollarIndexRawFn)(JSContext*, HandleString, int32_t*); +static const VMFunction GetFirstDollarIndexRawInfo = FunctionInfo(GetFirstDollarIndexRaw); + +void +CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) +{ + Register str = ToRegister(ins->str()); + Register output = ToRegister(ins->output()); + Register temp0 = ToRegister(ins->temp0()); + Register temp1 = ToRegister(ins->temp1()); + Register len = ToRegister(ins->temp2()); + + OutOfLineCode* ool = oolCallVM(GetFirstDollarIndexRawInfo, ins, ArgList(str), + StoreRegisterTo(output)); + + masm.branchIfRope(str, ool->entry()); + masm.loadStringLength(str, len); + + Label isLatin1, done; + masm.branchLatin1String(str, &isLatin1); + { + FindFirstDollarIndex(masm, str, len, temp0, temp1, output, /* isLatin1 = */ false); + } + masm.jump(&done); + { + masm.bind(&isLatin1); + FindFirstDollarIndex(masm, str, len, temp0, temp1, output, /* isLatin1 = */ true); + } + masm.bind(&done); + masm.bind(ool->rejoin()); +} + typedef JSString* (*StringReplaceFn)(JSContext*, HandleString, HandleString, HandleString); static const VMFunction StringFlatReplaceInfo = FunctionInfo(js::str_flat_replace_string); static const VMFunction StringReplaceInfo = FunctionInfo(StringReplace); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 89ea41782294..98f970a7a7f5 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -122,6 +122,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototypeOptimizable* ool); void visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* lir); void visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOptimizable* ool); + void visitGetFirstDollarIndex(LGetFirstDollarIndex* lir); void visitStringReplace(LStringReplace* lir); void emitSharedStub(ICStub::Kind kind, LInstruction* lir); void visitBinarySharedStub(LBinarySharedStub* lir); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 444d7371f2cd..3c62633ba88e 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -70,6 +70,7 @@ _(IsRegExpObject) \ _(RegExpPrototypeOptimizable) \ _(RegExpInstanceOptimizable) \ + _(GetFirstDollarIndex) \ \ _(String) \ _(StringCharCodeAt) \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index f87833bc8663..7dbc5e24f75e 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -842,6 +842,7 @@ class IonBuilder InliningStatus inlineIsRegExpObject(CallInfo& callInfo); InliningStatus inlineRegExpPrototypeOptimizable(CallInfo& callInfo); InliningStatus inlineRegExpInstanceOptimizable(CallInfo& callInfo); + InliningStatus inlineGetFirstDollarIndex(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 77ea17ab2d90..0feb7e01f4b8 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2315,6 +2315,17 @@ LIRGenerator::visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins) define(lir, ins); } +void +LIRGenerator::visitGetFirstDollarIndex(MGetFirstDollarIndex* ins) +{ + MOZ_ASSERT(ins->str()->type() == MIRType_String); + MOZ_ASSERT(ins->type() == MIRType_Int32); + LGetFirstDollarIndex* lir = new(alloc()) LGetFirstDollarIndex(useRegister(ins->str()), + temp(), temp(), temp()); + define(lir, ins); + assignSafepoint(lir, ins); +} + void LIRGenerator::visitStringReplace(MStringReplace* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 4e7240063216..5bc35fb6f0e2 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -168,6 +168,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitRegExpTester(MRegExpTester* ins); void visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins); void visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins); + void visitGetFirstDollarIndex(MGetFirstDollarIndex* ins); void visitStringReplace(MStringReplace* ins); void visitBinarySharedStub(MBinarySharedStub* ins); void visitUnarySharedStub(MUnarySharedStub* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 3eb594b388f7..4e220b9d4f45 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -188,6 +188,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineRegExpPrototypeOptimizable(callInfo); case InlinableNative::RegExpInstanceOptimizable: return inlineRegExpInstanceOptimizable(callInfo); + case InlinableNative::GetFirstDollarIndex: + return inlineGetFirstDollarIndex(callInfo); // String natives. case InlinableNative::String: @@ -1952,6 +1954,31 @@ IonBuilder::inlineRegExpInstanceOptimizable(CallInfo& callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineGetFirstDollarIndex(CallInfo& callInfo) +{ + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); + return InliningStatus_NotInlined; + } + + MDefinition* strArg = callInfo.getArg(0); + + if (strArg->type() != MIRType_String) + return InliningStatus_NotInlined; + + if (getInlineReturnType() != MIRType_Int32) + return InliningStatus_NotInlined; + + callInfo.setImplicitlyUsedUnchecked(); + + MInstruction* ins = MGetFirstDollarIndex::New(alloc(), strArg); + current->add(ins); + current->push(ins); + + return InliningStatus_Inlined; +} + IonBuilder::InliningStatus IonBuilder::inlineStringReplaceString(CallInfo& callInfo) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index b0ee5165e8d7..26483bfe055f 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8099,6 +8099,31 @@ class MRegExpInstanceOptimizable } }; +class MGetFirstDollarIndex + : public MUnaryInstruction, + public StringPolicy<0>::Data +{ + explicit MGetFirstDollarIndex(MDefinition* str) + : MUnaryInstruction(str) + { + setResultType(MIRType_Int32); + setMovable(); + } + + public: + INSTRUCTION_HEADER(GetFirstDollarIndex) + + static MGetFirstDollarIndex* New(TempAllocator& alloc, MDefinition* str) { + return new(alloc) MGetFirstDollarIndex(str); + } + MDefinition* str() const { + return getOperand(0); + } + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + class MStringReplace : public MTernaryInstruction, public Mix3Policy, StringPolicy<1>, StringPolicy<2> >::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index ed834249a98c..ebf8f3fc273c 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -151,6 +151,7 @@ namespace jit { _(RegExpTester) \ _(RegExpPrototypeOptimizable) \ _(RegExpInstanceOptimizable) \ + _(GetFirstDollarIndex) \ _(StringReplace) \ _(Lambda) \ _(LambdaArrow) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index f2cec0701c4e..70106064d4b5 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -4439,6 +4439,32 @@ class LRegExpInstanceOptimizable : public LInstructionHelper<1, 2, 1> } }; +class LGetFirstDollarIndex : public LInstructionHelper<1, 1, 3> +{ + public: + LIR_HEADER(GetFirstDollarIndex); + explicit LGetFirstDollarIndex(const LAllocation& str, const LDefinition& temp0, + const LDefinition& temp1, const LDefinition& temp2) { + setOperand(0, str); + setTemp(0, temp0); + setTemp(1, temp1); + setTemp(2, temp2); + } + + const LAllocation* str() { + return getOperand(0); + } + const LDefinition* temp0() { + return getTemp(0); + } + const LDefinition* temp1() { + return getTemp(1); + } + const LDefinition* temp2() { + return getTemp(2); + } +}; + class LStringReplace: 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 a65e08921e81..f220c4909206 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -211,6 +211,7 @@ _(RegExpTester) \ _(RegExpPrototypeOptimizable) \ _(RegExpInstanceOptimizable) \ + _(GetFirstDollarIndex) \ _(StringReplace) \ _(Substr) \ _(BinarySharedStub) \ diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 960de8371955..317338b3a2fc 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2568,7 +2568,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("RegExpEscapeMetaChars", intrinsic_RegExpEscapeMetaChars, 1,0), JS_FN("GetElemBaseForLambda", intrinsic_GetElemBaseForLambda, 1,0), JS_FN("GetStringDataProperty", intrinsic_GetStringDataProperty, 2,0), - JS_FN("GetFirstDollarIndex", GetFirstDollarIndex, 1,0), + JS_INLINABLE_FN("GetFirstDollarIndex", GetFirstDollarIndex, 1,0, + GetFirstDollarIndex), JS_FN("FlatStringMatch", FlatStringMatch, 2,0), JS_FN("FlatStringSearch", FlatStringSearch, 2,0),