Bug 1077514 - Execute regexps in the bytecode interpreter if the initial JIT execution was interrupted, r=jandem.

This commit is contained in:
Brian Hackett 2014-10-13 10:46:38 -07:00
parent 20095be902
commit 71f28b167a
4 changed files with 74 additions and 50 deletions

View File

@ -1650,7 +1650,7 @@ IsNativeRegExpEnabled(JSContext *cx)
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");
@ -1727,7 +1727,7 @@ irregexp::CompilePattern(JSContext *cx, RegExpShared *shared, RegExpCompileData
Maybe<InterpretedRegExpMacroAssembler> interpreted_assembler;
RegExpMacroAssembler *assembler;
if (IsNativeRegExpEnabled(cx)) {
if (IsNativeRegExpEnabled(cx) && !force_bytecode) {
NativeRegExpMacroAssembler::Mode mode =
is_ascii ? NativeRegExpMacroAssembler::ASCII
: NativeRegExpMacroAssembler::CHAR16;

View File

@ -88,7 +88,7 @@ struct RegExpCode
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.

View File

@ -455,14 +455,15 @@ RegExpShared::trace(JSTracer *trc)
}
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);
}
/*
@ -485,12 +486,12 @@ RegExpShared::compile(JSContext *cx, HandleLinearString input, CompilationMode m
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;
@ -517,25 +518,30 @@ RegExpShared::compile(JSContext *cx, HandleAtom pattern, HandleLinearString inpu
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());
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
@ -547,7 +553,7 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start,
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;
/*
@ -591,7 +597,63 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start,
return RegExpRunStatus_Success;
}
if (uint8_t *byteCode = compilation(mode, input->hasLatin1Chars()).byteCode) {
do {
jit::JitCode *code = compilation(mode, input->hasLatin1Chars()).jitCode;
if (!code)
break;
RegExpRunStatus result;
{
AutoTraceLog logJIT(logger, TraceLogger::IrregexpExecute);
AutoCheckCannotGC nogc;
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. 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;
break;
}
js_ReportOverRecursed(cx);
return RegExpRunStatus_Error;
}
if (result == RegExpRunStatus_Success_NotFound)
return RegExpRunStatus_Success_NotFound;
MOZ_ASSERT(result == RegExpRunStatus_Success);
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);
@ -612,55 +674,6 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start,
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.
bool interrupted;
{
JSRuntime::AutoLockForInterrupt lock(cx->runtime());
interrupted = cx->runtime()->interrupt;
}
if (interrupted) {
if (!InvokeInterruptCallback(cx))
return RegExpRunStatus_Error;
continue;
}
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;
}
size_t

View File

@ -106,6 +106,11 @@ class RegExpShared
MatchOnly
};
enum ForceByteCodeEnum {
DontForceByteCode,
ForceByteCode
};
private:
friend class RegExpCompartment;
friend class RegExpStatics;
@ -120,7 +125,9 @@ class RegExpShared
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. */
@ -145,10 +152,13 @@ class RegExpShared
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)];
@ -189,8 +199,9 @@ class RegExpShared
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)