diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2023-12-19 15:13:55 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2024-01-23 16:37:32 +0100 |
commit | 04820be3011395bd5cb13dea8f81c9b9bca4da8f (patch) | |
tree | 3df1086aabbe82e4146aad6552ccddab4d282038 /src/qml/jsruntime | |
parent | 65ffcb95500b77fd89f594b2db11cbc40d7b91bb (diff) |
RegExp: Do on demand JIT compilation
JIT compiling means that we need to obtain executable memory for the
jitted code. That has a non-neglibile cost (in addition to the runtime
cost of compiling the pattern).
Thus, only compile the pattern if we suspect it to be worthwhile:
- when we called match multiple times
- or when the string we match against is quite large
We also assume that the JIT generally works, and thus discard the
bytecode immediately after jitting – if the JIT fails once (either to
compile, or with offset failure), we recompile the bytecode and never
JIT again.
Change-Id: I91e9e10c5ab895af730dbb4a9bc85470dd794d1f
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4regexp.cpp | 76 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexp_p.h | 2 |
2 files changed, 52 insertions, 26 deletions
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index be7ff77603..1876b29c3e 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -9,6 +9,10 @@ using namespace QV4; +#if ENABLE(YARR_JIT) +static constexpr quint8 RegexpJitThreshold = 5; +#endif + static JSC::RegExpFlags jscFlags(uint flags) { JSC::RegExpFlags jscFlags = JSC::NoFlags; @@ -40,12 +44,51 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) if (!isValid()) return JSC::Yarr::offsetNoMatch; +#if ENABLE(YARR_JIT) + auto *priv = d(); + + auto regenerateByteCode = [](Heap::RegExp *regexp) { + JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(*regexp->pattern), jscFlags(regexp->flags), + error); + + // As we successfully parsed the pattern before, we should still be able to. + Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError); + + regexp->byteCode = JSC::Yarr::byteCompile( + yarrPattern, + regexp->internalClass->engine->bumperPointerAllocator).release(); + }; + auto removeJitCode = [](Heap::RegExp *regexp) { + delete regexp->jitCode; + regexp->jitCode = nullptr; + regexp->jitFailed = true; + }; + if (!(priv->jitCode || priv->jitFailed)) { + if (string.length() > 1024 || priv->matchCount++ == RegexpJitThreshold) { + auto *engine = priv->internalClass->engine; + if (engine->canJIT()) { + JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(*priv->pattern), jscFlags(priv->flags), error); + if (!yarrPattern.m_containsBackreferences) { + priv->jitCode = new JSC::Yarr::YarrCodeBlock; + JSC::VM *vm = static_cast<JSC::VM *>(engine); + JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *priv->jitCode); + } + } + if (!priv->hasValidJITCode()) + removeJitCode(priv); + } + // Note: We can't throw away the byte code as the JIT can't handle everything and + // might return offsetJITFail + } +#endif + WTF::String s(string); #if ENABLE(YARR_JIT) - static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1; - auto *priv = d(); if (priv->hasValidJITCode()) { + static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1; uint ret = JSC::Yarr::offsetNoMatch; #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) char buffer[8192]; @@ -58,19 +101,10 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) if (ret != offsetJITFail) return ret; + removeJitCode(priv); // JIT failed. We need byteCode to run the interpreter. - if (!priv->byteCode) { - JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; - JSC::Yarr::YarrPattern yarrPattern(WTF::String(*priv->pattern), jscFlags(priv->flags), - error); - - // As we successfully parsed the pattern before, we should still be able to. - Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError); - - priv->byteCode = JSC::Yarr::byteCompile( - yarrPattern, - priv->internalClass->engine->bumperPointerAllocator).release(); - } + Q_ASSERT(!priv->byteCode); + regenerateByteCode(priv); } #endif // ENABLE(YARR_JIT) @@ -175,25 +209,15 @@ void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, uint fl this->flags = flags; valid = false; + jitFailed = false; + matchCount = 0; JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags(flags), error); if (error != JSC::Yarr::ErrorCode::NoError) return; subPatternCount = yarrPattern.m_numSubpatterns; -#if ENABLE(YARR_JIT) - if (!yarrPattern.m_containsBackreferences && engine->canJIT()) { - jitCode = new JSC::Yarr::YarrCodeBlock; - JSC::VM *vm = static_cast<JSC::VM *>(engine); - JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *jitCode); - } -#else Q_UNUSED(engine); -#endif - if (hasValidJITCode()) { - valid = true; - return; - } byteCode = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator).release(); if (byteCode) valid = true; diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 1b70354caa..8a178dd2f6 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -66,6 +66,8 @@ struct RegExp : Base { int subPatternCount; uint flags; bool valid; + bool jitFailed; + quint8 matchCount; QString flagsAsString() const; int captureCount() const { return subPatternCount + 1; } |