Skip to content

Commit

Permalink
Avoid JIT compilation for simple attribute-only exact matching
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=267616
rdar://121082724

Reviewed by Ryosuke Niwa.

To reduce machine code size, we add a path which handles very simple attribute-only exact matching,
which can avoid code generation for that patterns.

* Source/WebCore/dom/SelectorQuery.cpp:
(WebCore::isSingleAttributeExactSelector):
(WebCore::canOptimizeSingleAttributeExactMatch):
(WebCore::SelectorDataList::SelectorDataList):
(WebCore::SelectorDataList::executeSingleAttributeExactSelectorData const):
(WebCore::SelectorDataList::execute const):
* Source/WebCore/dom/SelectorQuery.h:

Canonical link: https://commits.webkit.org/273157@main
  • Loading branch information
Constellation committed Jan 18, 2024
1 parent ba9ec2f commit a2cc914
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
75 changes: 75 additions & 0 deletions Source/WebCore/dom/SelectorQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
#include "CommonAtomStrings.h"
#include "ElementAncestorIteratorInlines.h"
#include "HTMLBaseElement.h"
#include "HTMLDocument.h"
#include "HTMLNames.h"
#include "SVGElement.h"
#include "SelectorChecker.h"
#include "StaticNodeList.h"
#include "StyledElement.h"
Expand All @@ -51,6 +53,12 @@ static bool isSingleClassNameSelector(const CSSSelector& selector)
{
return selector.isLastInTagHistory() && selector.match() == CSSSelector::Match::Class;
}

static bool isSingleAttributeExactSelector(const CSSSelector& selector)
{
return selector.isLastInTagHistory() && selector.match() == CSSSelector::Match::Exact;
}

#endif // ASSERT_ENABLED

enum class IdMatchingType : uint8_t {
Expand Down Expand Up @@ -89,6 +97,27 @@ static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
return IdMatchingType::None;
}

static bool canOptimizeSingleAttributeExactMatch(const CSSSelector& selector)
{
// Bailout if attribute name needs to be definitely case-insensitive.
if (selector.attributeValueMatchingIsCaseInsensitive())
return false;

const auto& attribute = selector.attribute();

if (!HTMLDocument::isCaseSensitiveAttribute(attribute))
return false;

// Bailout if we need to synchronize attributes.
if (Attribute::nameMatchesFilter(HTMLNames::styleAttr, attribute.prefix(), attribute.localNameLowercase(), attribute.namespaceURI()))
return false;

if (Attribute::nameMatchesFilter(SVGElement::animatableAttributeForName(attribute.localName()), attribute.prefix(), attribute.localName(), attribute.namespaceURI()))
return false;

return true;
}

SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
{
unsigned selectorCount = 0;
Expand All @@ -109,6 +138,14 @@ SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
case CSSSelector::Match::Class:
m_matchType = ClassNameMatch;
break;
case CSSSelector::Match::Exact:
if (canBeUsedForIdFastPath(selector))
m_matchType = RightMostWithIdMatch; // [id="name"] pattern goes here.
else if (canOptimizeSingleAttributeExactMatch(selector))
m_matchType = AttributeExactMatch;
else
m_matchType = CompilableSingle;
break;
default:
if (canBeUsedForIdFastPath(selector))
m_matchType = RightMostWithIdMatch;
Expand Down Expand Up @@ -365,6 +402,41 @@ ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const Co
}
}

template<typename OutputType>
ALWAYS_INLINE void SelectorDataList::executeSingleAttributeExactSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, OutputType& output) const
{
ASSERT(m_selectors.size() == 1);
ASSERT(isSingleAttributeExactSelector(*selectorData.selector));
ASSERT(canOptimizeSingleAttributeExactMatch(*selectorData.selector));

const auto& selectorAttribute = selectorData.selector->attribute();
const auto& selectorValue = selectorData.selector->value();
const auto& localNameLowercase = selectorAttribute.localNameLowercase();
const auto& localName = selectorAttribute.localName();
const auto& prefix = selectorAttribute.prefix();
const auto& namespaceURI = selectorAttribute.namespaceURI();

bool documentIsHTML = rootNode.document().isHTMLDocument();
for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) {
if (!element.hasAttributesWithoutUpdate())
continue;

bool isHTML = documentIsHTML && element.isHTMLElement();
const auto& localNameToMatch = isHTML ? localNameLowercase : localName;
for (const Attribute& attribute : element.attributesIterator()) {
if (!attribute.matches(prefix, localNameToMatch, namespaceURI))
continue;

if (selectorValue == attribute.value()) {
appendOutputForElement(output, element);
if constexpr (std::is_same_v<OutputType, Element*>)
return;
break;
}
}
}
}

template<typename OutputType>
ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, OutputType& output) const
{
Expand Down Expand Up @@ -556,6 +628,9 @@ ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, OutputType
case ClassNameMatch:
executeSingleClassNameSelectorData(*searchRootNode, m_selectors.first(), output);
break;
case AttributeExactMatch:
executeSingleAttributeExactSelectorData(*searchRootNode, m_selectors.first(), output);
break;
case CompilableMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
{
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/dom/SelectorQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class SelectorDataList {
template <typename OutputType> void executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData&, const CSSSelector* idSelector, OutputType&) const;
template <typename OutputType> void executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData&, OutputType&) const;
template <typename OutputType> void executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData&, OutputType&) const;
template <typename OutputType> void executeSingleAttributeExactSelectorData(const ContainerNode& rootNode, const SelectorData&, OutputType&) const;
template <typename OutputType> void executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData&, OutputType&) const;
template <typename OutputType> void executeSingleMultiSelectorData(const ContainerNode& rootNode, OutputType&) const;
#if ENABLE(CSS_SELECTOR_JIT)
Expand All @@ -89,6 +90,7 @@ class SelectorDataList {
RightMostWithIdMatch,
TagNameMatch,
ClassNameMatch,
AttributeExactMatch,
MultipleSelectorMatch,
} m_matchType;
};
Expand Down

0 comments on commit a2cc914

Please sign in to comment.