author | Brian Hackett <bhackett1024@gmail.com> |
Mon, 13 Oct 2014 10:46:38 -0700 | |
changeset 210202 | ba5d59a26a4705550dc63ad2c2ee8affccd2fe5a |
parent 210201 | 83c786296b02163be98097e8d222d706716443a8 |
child 210203 | b3cf4699f5380efa15c40913a69f9dc9bbbbf06a |
push id | 27645 |
push user | kwierso@gmail.com |
push date | Tue, 14 Oct 2014 01:50:26 +0000 |
treeherder | mozilla-central@54217864bae9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 1077514 |
milestone | 35.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -1645,17 +1645,17 @@ IsNativeRegExpEnabled(JSContext *cx) #else return cx->runtime()->options().nativeRegExp(); #endif } RegExpCode irregexp::CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData *data, HandleLinearString sample, bool is_global, bool ignore_case, - bool is_ascii, bool match_only) + bool is_ascii, bool match_only, bool force_bytecode) { if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) { JS_ReportError(cx, "regexp too big"); return RegExpCode(); } LifoAlloc &alloc = cx->tempLifoAlloc(); RegExpCompiler compiler(cx, &alloc, data->capture_count, ignore_case, is_ascii, match_only); @@ -1722,17 +1722,17 @@ irregexp::CompilePattern(JSContext *cx, return RegExpCode(); } Maybe<jit::IonContext> ctx; Maybe<NativeRegExpMacroAssembler> native_assembler; Maybe<InterpretedRegExpMacroAssembler> interpreted_assembler; RegExpMacroAssembler *assembler; - if (IsNativeRegExpEnabled(cx)) { + if (IsNativeRegExpEnabled(cx) && !force_bytecode) { NativeRegExpMacroAssembler::Mode mode = is_ascii ? NativeRegExpMacroAssembler::ASCII : NativeRegExpMacroAssembler::CHAR16; ctx.emplace(cx, (jit::TempAllocator *) nullptr); native_assembler.emplace(&alloc, shared, cx->runtime(), mode, (data->capture_count + 1) * 2); assembler = native_assembler.ptr(); } else {
--- a/js/src/irregexp/RegExpEngine.h +++ b/js/src/irregexp/RegExpEngine.h @@ -83,17 +83,17 @@ struct RegExpCode void destroy() { js_free(byteCode); } }; RegExpCode CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData *data, HandleLinearString sample, bool is_global, bool ignore_case, - bool is_ascii, bool match_only); + bool is_ascii, bool match_only, bool force_bytecode); // Note: this may return RegExpRunStatus_Error if an interrupt was requested // while the code was executing. template <typename CharT> RegExpRunStatus ExecuteCode(JSContext *cx, jit::JitCode *codeBlock, const CharT *chars, size_t start, size_t length, MatchPairs *matches);
--- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -450,24 +450,25 @@ RegExpShared::trace(JSTracer *trc) for (size_t i = 0; i < ArrayLength(compilationArray); i++) { RegExpCompilation &compilation = compilationArray[i]; if (compilation.jitCode) MarkJitCode(trc, &compilation.jitCode, "RegExpShared code"); } } bool -RegExpShared::compile(JSContext *cx, HandleLinearString input, CompilationMode mode) +RegExpShared::compile(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force) { TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); AutoTraceLog logCompile(logger, TraceLogger::IrregexpCompile); if (!sticky()) { RootedAtom pattern(cx, source); - return compile(cx, pattern, input, mode); + return compile(cx, pattern, input, mode, force); } /* * The sticky case we implement hackily by prepending a caret onto the front * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp. */ static const char prefix[] = {'^', '(', '?', ':'}; static const char postfix[] = {')'}; @@ -480,22 +481,22 @@ RegExpShared::compile(JSContext *cx, Han if (!sb.append(source)) return false; sb.infallibleAppend(postfix, ArrayLength(postfix)); RootedAtom fakeySource(cx, sb.finishAtom()); if (!fakeySource) return false; - return compile(cx, fakeySource, input, mode); + return compile(cx, fakeySource, input, mode, force); } bool RegExpShared::compile(JSContext *cx, HandleAtom pattern, HandleLinearString input, - CompilationMode mode) + CompilationMode mode, ForceByteCodeEnum force) { if (!ignoreCase() && !StringHasRegExpMetaChars(pattern)) { canStringMatch = true; parenCount = 0; return true; } CompileOptions options(cx); @@ -512,47 +513,52 @@ RegExpShared::compile(JSContext *cx, Han } this->parenCount = data.capture_count; irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input, false /* global() */, ignoreCase(), input->hasLatin1Chars(), - mode == MatchOnly); + mode == MatchOnly, + force == ForceByteCode); if (code.empty()) return false; MOZ_ASSERT(!code.jitCode || !code.byteCode); + MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode); RegExpCompilation &compilation = this->compilation(mode, input->hasLatin1Chars()); - compilation.jitCode = code.jitCode; - compilation.byteCode = code.byteCode; + if (code.jitCode) + compilation.jitCode = code.jitCode; + else if (code.byteCode) + compilation.byteCode = code.byteCode; return true; } bool -RegExpShared::compileIfNecessary(JSContext *cx, HandleLinearString input, CompilationMode mode) +RegExpShared::compileIfNecessary(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force) { - if (isCompiled(mode, input->hasLatin1Chars()) || canStringMatch) + if (isCompiled(mode, input->hasLatin1Chars(), force) || canStringMatch) return true; - return compile(cx, input, mode); + return compile(cx, input, mode, force); } RegExpRunStatus RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start, MatchPairs *matches) { TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); CompilationMode mode = matches ? Normal : MatchOnly; /* Compile the code at point-of-use. */ - if (!compileIfNecessary(cx, input, mode)) + if (!compileIfNecessary(cx, input, mode, DontForceByteCode)) return RegExpRunStatus_Error; /* * Ensure sufficient memory for output vector. * No need to initialize it. The RegExp engine fills them in on a match. */ if (matches && !matches->allocOrExpandArray(pairCount())) return RegExpRunStatus_Error; @@ -586,86 +592,93 @@ RegExpShared::execute(JSContext *cx, Han (*matches)[0].start = res; (*matches)[0].limit = res + source->length(); matches->checkAgainst(origLength); } return RegExpRunStatus_Success; } - if (uint8_t *byteCode = compilation(mode, input->hasLatin1Chars()).byteCode) { - AutoTraceLog logInterpreter(logger, TraceLogger::IrregexpExecute); - - AutoStableStringChars inputChars(cx); - if (!inputChars.init(cx, input)) - return RegExpRunStatus_Error; + do { + jit::JitCode *code = compilation(mode, input->hasLatin1Chars()).jitCode; + if (!code) + break; RegExpRunStatus result; - if (inputChars.isLatin1()) { - const Latin1Char *chars = inputChars.latin1Range().start().get() + charsOffset; - result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); - } else { - const char16_t *chars = inputChars.twoByteRange().start().get() + charsOffset; - result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); - } - - if (result == RegExpRunStatus_Success && matches) { - matches->displace(displacement); - matches->checkAgainst(origLength); - } - return result; - } - - while (true) { - RegExpRunStatus result; { AutoTraceLog logJIT(logger, TraceLogger::IrregexpExecute); AutoCheckCannotGC nogc; - jit::JitCode *code = compilation(mode, input->hasLatin1Chars()).jitCode; if (input->hasLatin1Chars()) { const Latin1Char *chars = input->latin1Chars(nogc) + charsOffset; result = irregexp::ExecuteCode(cx, code, chars, start, length, matches); } else { const char16_t *chars = input->twoByteChars(nogc) + charsOffset; result = irregexp::ExecuteCode(cx, code, chars, start, length, matches); } } if (result == RegExpRunStatus_Error) { // The RegExp engine might exit with an exception if an interrupt - // was requested. Check this case and retry until a clean result is - // obtained. + // was requested. If this happens, break out and retry the regexp + // in the bytecode interpreter, which can execute while tolerating + // future interrupts. Otherwise, if we keep getting interrupted we + // will never finish executing the regexp. bool interrupted; { JSRuntime::AutoLockForInterrupt lock(cx->runtime()); interrupted = cx->runtime()->interrupt; } if (interrupted) { if (!InvokeInterruptCallback(cx)) return RegExpRunStatus_Error; - continue; + break; } js_ReportOverRecursed(cx); return RegExpRunStatus_Error; } if (result == RegExpRunStatus_Success_NotFound) return RegExpRunStatus_Success_NotFound; MOZ_ASSERT(result == RegExpRunStatus_Success); - break; + + if (matches) { + matches->displace(displacement); + matches->checkAgainst(origLength); + } + return RegExpRunStatus_Success; + } while (false); + + // Compile bytecode for the RegExp if necessary. + if (!compileIfNecessary(cx, input, mode, ForceByteCode)) + return RegExpRunStatus_Error; + + uint8_t *byteCode = compilation(mode, input->hasLatin1Chars()).byteCode; + AutoTraceLog logInterpreter(logger, TraceLogger::IrregexpExecute); + + AutoStableStringChars inputChars(cx); + if (!inputChars.init(cx, input)) + return RegExpRunStatus_Error; + + RegExpRunStatus result; + if (inputChars.isLatin1()) { + const Latin1Char *chars = inputChars.latin1Range().start().get() + charsOffset; + result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); + } else { + const char16_t *chars = inputChars.twoByteRange().start().get() + charsOffset; + result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches); } - if (matches) { + if (result == RegExpRunStatus_Success && matches) { matches->displace(displacement); matches->checkAgainst(origLength); } - return RegExpRunStatus_Success; + return result; } size_t RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = mallocSizeOf(this); for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
--- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -101,31 +101,38 @@ CloneRegExpObject(JSContext *cx, JSObjec class RegExpShared { public: enum CompilationMode { Normal, MatchOnly }; + enum ForceByteCodeEnum { + DontForceByteCode, + ForceByteCode + }; + private: friend class RegExpCompartment; friend class RegExpStatics; typedef frontend::TokenStream TokenStream; struct RegExpCompilation { HeapPtrJitCode jitCode; uint8_t *byteCode; RegExpCompilation() : byteCode(nullptr) {} ~RegExpCompilation() { js_free(byteCode); } - bool compiled() const { return jitCode || byteCode; } + bool compiled(ForceByteCodeEnum force = DontForceByteCode) const { + return byteCode || (force == DontForceByteCode && jitCode); + } }; /* Source to the RegExp, for lazy compilation. */ HeapPtrAtom source; RegExpFlag flags; size_t parenCount; bool canStringMatch; @@ -140,20 +147,23 @@ class RegExpShared } MOZ_CRASH(); } // Tables referenced by JIT code. Vector<uint8_t *, 0, SystemAllocPolicy> tables; /* Internal functions. */ - bool compile(JSContext *cx, HandleLinearString input, CompilationMode mode); - bool compile(JSContext *cx, HandleAtom pattern, HandleLinearString input, CompilationMode mode); + bool compile(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force); + bool compile(JSContext *cx, HandleAtom pattern, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force); - bool compileIfNecessary(JSContext *cx, HandleLinearString input, CompilationMode mode); + bool compileIfNecessary(JSContext *cx, HandleLinearString input, + CompilationMode mode, ForceByteCodeEnum force); const RegExpCompilation &compilation(CompilationMode mode, bool latin1) const { return compilationArray[CompilationIndex(mode, latin1)]; } RegExpCompilation &compilation(CompilationMode mode, bool latin1) { return compilationArray[CompilationIndex(mode, latin1)]; } @@ -184,18 +194,19 @@ class RegExpShared JSAtom *getSource() const { return source; } RegExpFlag getFlags() const { return flags; } bool ignoreCase() const { return flags & IgnoreCaseFlag; } bool global() const { return flags & GlobalFlag; } bool multiline() const { return flags & MultilineFlag; } bool sticky() const { return flags & StickyFlag; } - bool isCompiled(CompilationMode mode, bool latin1) const { - return compilation(mode, latin1).compiled(); + bool isCompiled(CompilationMode mode, bool latin1, + ForceByteCodeEnum force = DontForceByteCode) const { + return compilation(mode, latin1).compiled(force); } bool isCompiled() const { return isCompiled(Normal, true) || isCompiled(Normal, false) || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false); } void trace(JSTracer *trc);