From ece699ddc4d1e1682e39bb0a05346134b8bebee1 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 19 Dec 2023 15:13:55 +0100 Subject: RegExp: Do on demand JIT compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 before jitting – if the JIT fails once (either to compile, or with offset failure), we recompile the bytecode and never JIT this pattern again. Amends commit 04820be3011395bd5cb13dea8f81c9b9bca4da8f. Change-Id: I7aed4c0fdd85f1c69bc16cb2caa384aeabd62af2 Reviewed-by: Ulf Hermann Reviewed-by: Sami Shalayel Reviewed-by: Semih Yavuz Reviewed-by: Fabian Kosmale --- src/qml/jsruntime/qv4regexp.cpp | 83 ++++++++++++++++++++++++++++------------- src/qml/jsruntime/qv4regexp_p.h | 2 + 2 files changed, 59 insertions(+), 26 deletions(-) (limited to 'src/qml/jsruntime') diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index be7ff77603..4629a409ac 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,58 @@ 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; + }; + + auto removeByteCode = [](Heap::RegExp *regexp) { + delete regexp->byteCode; + regexp->byteCode = nullptr; + }; + + if (!priv->jitCode && !priv->jitFailed && priv->internalClass->engine->canJIT() + && (string.length() > 1024 || priv->matchCount++ == RegexpJitThreshold)) { + removeByteCode(priv); + + 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(priv->internalClass->engine); + JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *priv->jitCode); + } + + if (!priv->hasValidJITCode()) { + removeJitCode(priv); + regenerateByteCode(priv); + } + } +#endif + WTF::String s(string); #if ENABLE(YARR_JIT) - static const uint offsetJITFail = std::numeric_limits::max() - 1; - auto *priv = d(); if (priv->hasValidJITCode()) { + static const uint offsetJITFail = std::numeric_limits::max() - 1; uint ret = JSC::Yarr::offsetNoMatch; #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) char buffer[8192]; @@ -58,19 +108,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 +216,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(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; } -- cgit v1.2.3