Skip to content

Commit

Permalink
Merge r228313 - Move compiled selectors to StyleRule
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=182602

Reviewed by Zalan Bujtas.

Currently they are owned by RuleData. Several RuleData objects can refer to the same StyleRule, requiring recompilation.
Compiled selectors are context-independent so they can be shared between all clients.

* WebCore.xcodeproj/project.pbxproj:
* css/CSSSelectorList.cpp:
(WebCore::CSSSelectorList::listSize const):

Compute the number of complex selectors on the list.

* css/CSSSelectorList.h:
* css/DocumentRuleSets.cpp:
(WebCore::makeRuleSet):
(WebCore::DocumentRuleSets::classInvalidationRuleSets const):

Pass around list index along with the selector index (compiled selectors are found by list index).

* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::ruleMatches):
* css/RuleSet.cpp:
(WebCore::RuleData::RuleData):
* css/RuleSet.h:
(WebCore::RuleData::compilationStatus const): Deleted.
(WebCore::RuleData::compiledSelectorCodeRef const): Deleted.
(WebCore::RuleData::setCompiledSelector const): Deleted.
(WebCore::RuleData::~RuleData): Deleted.
(WebCore::RuleData::compiledSelectorUsed const): Deleted.
* css/StyleRule.cpp:
(WebCore::StyleRule::StyleRule):
* css/StyleRule.h:

    Add CompiledSelector member.

* cssjit/CompiledSelector.h: Added.

    Move to a header of its own to keeps dependencies simple.

(WebCore::SelectorCompilationStatus::SelectorCompilationStatus):
(WebCore::SelectorCompilationStatus::operator Status const):
* cssjit/SelectorCompiler.h:
(): Deleted.
(WebCore::SelectorCompilationStatus::SelectorCompilationStatus): Deleted.
(WebCore::SelectorCompilationStatus::operator Status const): Deleted.
  • Loading branch information
anttijk authored and carlosgcampos committed Feb 19, 2018
1 parent ad48c36 commit 3c7384a
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 94 deletions.
50 changes: 50 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,53 @@
2018-02-09 Antti Koivisto <antti@apple.com>

Move compiled selectors to StyleRule
https://bugs.webkit.org/show_bug.cgi?id=182602

Reviewed by Zalan Bujtas.

Currently they are owned by RuleData. Several RuleData objects can refer to the same StyleRule, requiring recompilation.
Compiled selectors are context-independent so they can be shared between all clients.

* WebCore.xcodeproj/project.pbxproj:
* css/CSSSelectorList.cpp:
(WebCore::CSSSelectorList::listSize const):

Compute the number of complex selectors on the list.

* css/CSSSelectorList.h:
* css/DocumentRuleSets.cpp:
(WebCore::makeRuleSet):
(WebCore::DocumentRuleSets::classInvalidationRuleSets const):

Pass around list index along with the selector index (compiled selectors are found by list index).

* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::ruleMatches):
* css/RuleSet.cpp:
(WebCore::RuleData::RuleData):
* css/RuleSet.h:
(WebCore::RuleData::compilationStatus const): Deleted.
(WebCore::RuleData::compiledSelectorCodeRef const): Deleted.
(WebCore::RuleData::setCompiledSelector const): Deleted.
(WebCore::RuleData::~RuleData): Deleted.
(WebCore::RuleData::compiledSelectorUsed const): Deleted.
* css/StyleRule.cpp:
(WebCore::StyleRule::StyleRule):
* css/StyleRule.h:

Add CompiledSelector member.

* cssjit/CompiledSelector.h: Added.

Move to a header of its own to keeps dependencies simple.

(WebCore::SelectorCompilationStatus::SelectorCompilationStatus):
(WebCore::SelectorCompilationStatus::operator Status const):
* cssjit/SelectorCompiler.h:
(): Deleted.
(WebCore::SelectorCompilationStatus::SelectorCompilationStatus): Deleted.
(WebCore::SelectorCompilationStatus::operator Status const): Deleted.

2018-02-08 Antoine Quint <graouts@apple.com>

[Web Animations] Always expose "composite" in output of getKeyframes()
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/WebCore.xcodeproj/project.pbxproj
Expand Up @@ -14007,6 +14007,7 @@
E43AF8E41AC5B7DD00CA717E /* CacheValidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CacheValidation.cpp; sourceTree = "<group>"; };
E43AF8E51AC5B7DD00CA717E /* CacheValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheValidation.h; sourceTree = "<group>"; };
E440AA951C68420800A265CC /* ElementAndTextDescendantIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementAndTextDescendantIterator.h; sourceTree = "<group>"; };
E4451077202C7E0100657D33 /* CompiledSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompiledSelector.h; sourceTree = "<group>"; };
E446138F0CD6331000FADA75 /* HTMLAudioElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLAudioElement.cpp; sourceTree = "<group>"; };
E44613900CD6331000FADA75 /* HTMLAudioElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLAudioElement.h; sourceTree = "<group>"; };
E44613910CD6331000FADA75 /* HTMLAudioElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLAudioElement.idl; sourceTree = "<group>"; };
Expand Down Expand Up @@ -15974,6 +15975,7 @@
26B9998D1803ADFA00D01121 /* cssjit */ = {
isa = PBXGroup;
children = (
E4451077202C7E0100657D33 /* CompiledSelector.h */,
26B999921803B9D900D01121 /* FunctionCall.h */,
26B9998E1803AE7200D01121 /* RegisterAllocator.h */,
26B999941804D54200D01121 /* SelectorCompiler.cpp */,
Expand Down
14 changes: 14 additions & 0 deletions Source/WebCore/css/CSSSelectorList.cpp
Expand Up @@ -95,6 +95,20 @@ unsigned CSSSelectorList::componentCount() const
return (current - m_selectorArray) + 1;
}

unsigned CSSSelectorList::listSize() const
{
if (!m_selectorArray)
return 0;
unsigned size = 1;
CSSSelector* current = m_selectorArray;
while (!current->isLastInSelectorList()) {
if (current->isLastInTagHistory())
++size;
++current;
}
return size;
}

CSSSelectorList& CSSSelectorList::operator=(CSSSelectorList&& other)
{
deleteSelectors();
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/css/CSSSelectorList.h
Expand Up @@ -66,6 +66,7 @@ class CSSSelectorList {
void buildSelectorsText(StringBuilder&) const;

unsigned componentCount() const;
unsigned listSize() const;

CSSSelectorList& operator=(CSSSelectorList&&);

Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/css/DocumentRuleSets.cpp
Expand Up @@ -114,7 +114,7 @@ static std::unique_ptr<RuleSet> makeRuleSet(const Vector<RuleFeature>& rules)
return nullptr;
auto ruleSet = std::make_unique<RuleSet>();
for (size_t i = 0; i < size; ++i)
ruleSet->addRule(rules[i].rule, rules[i].selectorIndex);
ruleSet->addRule(rules[i].rule, rules[i].selectorIndex, rules[i].selectorListIndex);
ruleSet->shrinkToFit();
return ruleSet;
}
Expand Down Expand Up @@ -187,7 +187,7 @@ static Vector<InvalidationRuleSet>* ensureInvalidationRuleSets(const AtomicStrin
auto& ruleSet = matchElementArray[arrayIndex];
if (!ruleSet)
ruleSet = std::make_unique<RuleSet>();
ruleSet->addRule(feature.rule, feature.selectorIndex);
ruleSet->addRule(feature.rule, feature.selectorIndex, feature.selectorListIndex);
if (feature.invalidationSelector)
invalidationSelectorArray[arrayIndex].append(feature.invalidationSelector);
}
Expand Down
24 changes: 11 additions & 13 deletions Source/WebCore/css/ElementRuleCollector.cpp
Expand Up @@ -383,18 +383,16 @@ inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned
}

#if ENABLE(CSS_SELECTOR_JIT)
void* compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress();
if (!compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::NotCompiled) {
SelectorCompilationStatus compilationStatus;
JSC::MacroAssemblerCodeRef compiledSelectorCodeRef;
compilationStatus = SelectorCompiler::compileSelector(ruleData.selector(), SelectorCompiler::SelectorContext::RuleCollector, compiledSelectorCodeRef);

ruleData.setCompiledSelector(compilationStatus, compiledSelectorCodeRef);
compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress();
auto& compiledSelector = ruleData.rule()->compiledSelectorForListIndex(ruleData.selectorListIndex());
void* compiledSelectorChecker = compiledSelector.codeRef.code().executableAddress();
if (!compiledSelectorChecker && compiledSelector.status == SelectorCompilationStatus::NotCompiled) {
compiledSelector.status = SelectorCompiler::compileSelector(ruleData.selector(), SelectorCompiler::SelectorContext::RuleCollector, compiledSelector.codeRef);

compiledSelectorChecker = compiledSelector.codeRef.code().executableAddress();
}

if (compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) {
SelectorCompiler::RuleCollectorSimpleSelectorChecker selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus());
if (compiledSelectorChecker && compiledSelector.status == SelectorCompilationStatus::SimpleSelectorChecker) {
auto selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, compiledSelector.status);
#if !ASSERT_MSG_DISABLED
unsigned ignoreSpecificity;
ASSERT_WITH_MESSAGE(!selectorChecker(&m_element, &ignoreSpecificity) || m_pseudoStyleRequest.pseudoId == NOPSEUDO, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything.");
Expand All @@ -420,12 +418,12 @@ inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned
bool selectorMatches;
#if ENABLE(CSS_SELECTOR_JIT)
if (compiledSelectorChecker) {
ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
ASSERT(compiledSelector.status == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);

SelectorCompiler::RuleCollectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus());
auto selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, compiledSelector.status);

#if CSS_SELECTOR_JIT_PROFILING
ruleData.compiledSelectorUsed();
compiledSelector.useCount++;
#endif
selectorMatches = selectorChecker(&m_element, &context, &specificity);
} else
Expand Down
9 changes: 4 additions & 5 deletions Source/WebCore/css/RuleFeature.cpp
Expand Up @@ -168,24 +168,23 @@ void RuleFeatureSet::collectFeatures(const RuleData& ruleData)
SelectorFeatures selectorFeatures;
recursivelyCollectFeaturesFromSelector(selectorFeatures, *ruleData.selector());
if (selectorFeatures.hasSiblingSelector)
siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex()));
siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.selectorListIndex()));
if (ruleData.containsUncommonAttributeSelector())
uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex()));
uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.selectorListIndex()));

for (auto& nameAndMatch : selectorFeatures.classes) {
classRules.ensure(nameAndMatch.first, [] {
return std::make_unique<Vector<RuleFeature>>();
}).iterator->value->append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), nameAndMatch.second));
}).iterator->value->append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.selectorListIndex(), nameAndMatch.second));
if (nameAndMatch.second == MatchElement::Host)
classesAffectingHost.add(nameAndMatch.first);
}

for (auto& selectorAndMatch : selectorFeatures.attributes) {
auto* selector = selectorAndMatch.first;
auto matchElement = selectorAndMatch.second;
attributeRules.ensure(selector->attribute().localName().convertToASCIILowercase(), [] {
return std::make_unique<Vector<RuleFeature>>();
}).iterator->value->append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), matchElement, selector));
}).iterator->value->append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.selectorListIndex(), matchElement, selector));
if (matchElement == MatchElement::Host)
attributesAffectingHost.add(selector->attribute().localName().convertToASCIILowercase());
}
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/css/RuleFeature.h
Expand Up @@ -37,15 +37,17 @@ enum class MatchElement { Subject, Parent, Ancestor, DirectSibling, IndirectSibl
constexpr unsigned matchElementCount = static_cast<unsigned>(MatchElement::Host) + 1;

struct RuleFeature {
RuleFeature(StyleRule* rule, unsigned selectorIndex, std::optional<MatchElement> matchElement = std::nullopt, const CSSSelector* invalidationSelector = nullptr)
RuleFeature(StyleRule* rule, unsigned selectorIndex, unsigned selectorListIndex, std::optional<MatchElement> matchElement = std::nullopt, const CSSSelector* invalidationSelector = nullptr)
: rule(rule)
, selectorIndex(selectorIndex)
, selectorListIndex(selectorListIndex)
, matchElement(matchElement)
, invalidationSelector(invalidationSelector)
{
}
StyleRule* rule;
unsigned selectorIndex;
unsigned selectorListIndex;
std::optional<MatchElement> matchElement;
const CSSSelector* invalidationSelector;
};
Expand Down
13 changes: 6 additions & 7 deletions Source/WebCore/css/RuleSet.cpp
Expand Up @@ -148,19 +148,17 @@ static inline PropertyWhitelistType determinePropertyWhitelistType(const CSSSele
return PropertyWhitelistNone;
}

RuleData::RuleData(StyleRule* rule, unsigned selectorIndex, unsigned position)
RuleData::RuleData(StyleRule* rule, unsigned selectorIndex, unsigned selectorListIndex, unsigned position)
: m_rule(rule)
, m_selectorIndex(selectorIndex)
, m_selectorListIndex(selectorListIndex)
, m_position(position)
, m_matchBasedOnRuleHash(static_cast<unsigned>(computeMatchBasedOnRuleHash(*selector())))
, m_canMatchPseudoElement(selectorCanMatchPseudoElement(*selector()))
, m_containsUncommonAttributeSelector(WebCore::containsUncommonAttributeSelector(*selector()))
, m_linkMatchType(SelectorChecker::determineLinkMatchType(selector()))
, m_propertyWhitelistType(determinePropertyWhitelistType(selector()))
, m_descendantSelectorIdentifierHashes(SelectorFilter::collectHashes(*selector()))
#if ENABLE(CSS_SELECTOR_JIT) && CSS_SELECTOR_JIT_PROFILING
, m_compiledSelectorUseCount(0)
#endif
{
ASSERT(m_position == position);
ASSERT(m_selectorIndex == selectorIndex);
Expand Down Expand Up @@ -201,9 +199,9 @@ static bool isHostSelectorMatchingInShadowTree(const CSSSelector& startSelector)
return leftmostSelector->match() == CSSSelector::PseudoClass && leftmostSelector->pseudoClassType() == CSSSelector::PseudoClassHost;
}

void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex)
void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, unsigned selectorListIndex)
{
RuleData ruleData(rule, selectorIndex, m_ruleCount++);
RuleData ruleData(rule, selectorIndex, selectorListIndex, m_ruleCount++);
m_features.collectFeatures(ruleData);

unsigned classBucketSize = 0;
Expand Down Expand Up @@ -404,8 +402,9 @@ void RuleSet::addRulesFromSheet(StyleSheetContents& sheet, const MediaQueryEvalu

void RuleSet::addStyleRule(StyleRule* rule)
{
unsigned selectorListIndex = 0;
for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = rule->selectorList().indexOfNextSelectorAfter(selectorIndex))
addRule(rule, selectorIndex);
addRule(rule, selectorIndex, selectorListIndex++);
}

bool RuleSet::hasShadowPseudoElementRules() const
Expand Down
42 changes: 5 additions & 37 deletions Source/WebCore/css/RuleSet.h
Expand Up @@ -59,12 +59,13 @@ class RuleData {
public:
static const unsigned maximumSelectorComponentCount = 8192;

RuleData(StyleRule*, unsigned selectorIndex, unsigned position);
RuleData(StyleRule*, unsigned selectorIndex, unsigned selectorListIndex, unsigned position);

unsigned position() const { return m_position; }
StyleRule* rule() const { return m_rule.get(); }
const CSSSelector* selector() const { return m_rule->selectorList().selectorAt(m_selectorIndex); }
unsigned selectorIndex() const { return m_selectorIndex; }
unsigned selectorListIndex() const { return m_selectorListIndex; }

bool canMatchPseudoElement() const { return m_canMatchPseudoElement; }
MatchBasedOnRuleHash matchBasedOnRuleHash() const { return static_cast<MatchBasedOnRuleHash>(m_matchBasedOnRuleHash); }
Expand All @@ -75,27 +76,10 @@ class RuleData {

void disableSelectorFiltering() { m_descendantSelectorIdentifierHashes[0] = 0; }

#if ENABLE(CSS_SELECTOR_JIT)
SelectorCompilationStatus compilationStatus() const { return m_compilationStatus; }
JSC::MacroAssemblerCodeRef compiledSelectorCodeRef() const { return m_compiledSelectorCodeRef; }
void setCompiledSelector(SelectorCompilationStatus status, JSC::MacroAssemblerCodeRef codeRef) const
{
m_compilationStatus = status;
m_compiledSelectorCodeRef = codeRef;
}
#if CSS_SELECTOR_JIT_PROFILING
~RuleData()
{
if (m_compiledSelectorCodeRef.code().executableAddress())
dataLogF("RuleData compiled selector %d \"%s\"\n", m_compiledSelectorUseCount, selector()->selectorText().utf8().data());
}
void compiledSelectorUsed() const { m_compiledSelectorUseCount++; }
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)

private:
RefPtr<StyleRule> m_rule;
unsigned m_selectorIndex : 13;
unsigned m_selectorIndex : 16;
unsigned m_selectorListIndex : 16;
// This number was picked fairly arbitrarily. We can probably lower it if we need to.
// Some simple testing showed <100,000 RuleData's on large sites.
unsigned m_position : 18;
Expand All @@ -105,25 +89,9 @@ class RuleData {
unsigned m_linkMatchType : 2; // SelectorChecker::LinkMatchMask
unsigned m_propertyWhitelistType : 2;
SelectorFilter::Hashes m_descendantSelectorIdentifierHashes;
#if ENABLE(CSS_SELECTOR_JIT)
mutable SelectorCompilationStatus m_compilationStatus;
mutable JSC::MacroAssemblerCodeRef m_compiledSelectorCodeRef;
#if CSS_SELECTOR_JIT_PROFILING
mutable unsigned m_compiledSelectorUseCount;
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)
};

struct SameSizeAsRuleData {
#if ENABLE(CSS_SELECTOR_JIT)
unsigned compilationStatus;
void* compiledSelectorPointer;
void* codeRefPtr;
#if CSS_SELECTOR_JIT_PROFILING
unsigned compiledSelectorUseCount;
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)

void* a;
unsigned b;
unsigned c;
Expand Down Expand Up @@ -152,7 +120,7 @@ class RuleSet {
void addRulesFromSheet(StyleSheetContents&, const MediaQueryEvaluator&, StyleResolver* = 0);

void addStyleRule(StyleRule*);
void addRule(StyleRule*, unsigned selectorIndex);
void addRule(StyleRule*, unsigned selectorIndex, unsigned selectorListIndex);
void addPageRule(StyleRulePage*);
void addToRuleSet(const AtomicString& key, AtomRuleMap&, const RuleData&);
void shrinkToFit();
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/css/StyleRule.cpp
Expand Up @@ -191,6 +191,9 @@ StyleRule::StyleRule(const StyleRule& o)
: StyleRuleBase(o)
, m_properties(o.properties().mutableCopy())
, m_selectorList(o.m_selectorList)
#if ENABLE(CSS_SELECTOR_JIT)
, m_compiledSelectors(o.m_compiledSelectors)
#endif
{
}

Expand Down
14 changes: 14 additions & 0 deletions Source/WebCore/css/StyleRule.h
Expand Up @@ -22,6 +22,7 @@
#pragma once

#include "CSSSelectorList.h"
#include "CompiledSelector.h"
#include "StyleProperties.h"
#include <wtf/RefPtr.h>
#include <wtf/TypeCasts.h>
Expand Down Expand Up @@ -139,6 +140,15 @@ class StyleRule final : public StyleRuleBase {

Vector<RefPtr<StyleRule>> splitIntoMultipleRulesWithMaximumSelectorComponentCount(unsigned) const;

#if ENABLE(CSS_SELECTOR_JIT)
CompiledSelector& compiledSelectorForListIndex(unsigned index)
{
if (m_compiledSelectors.isEmpty())
m_compiledSelectors.grow(m_selectorList.listSize());
return m_compiledSelectors[index];
}
#endif

static unsigned averageSizeInBytes();

private:
Expand All @@ -149,6 +159,10 @@ class StyleRule final : public StyleRuleBase {

mutable Ref<StylePropertiesBase> m_properties;
CSSSelectorList m_selectorList;

#if ENABLE(CSS_SELECTOR_JIT)
Vector<CompiledSelector> m_compiledSelectors;
#endif
};

inline const StyleProperties* StyleRule::propertiesWithoutDeferredParsing() const
Expand Down

0 comments on commit 3c7384a

Please sign in to comment.