Ignore:
Timestamp:
Jun 7, 2021, 6:43:14 PM (4 years ago)
Author:
Alexey Shvayka
Message:

Introduce LexicalScopeFeatures to enable future bytecode optimizations
https://bugs.webkit.org/show_bug.cgi?id=224072

Reviewed by Keith Miller.

Before this patch, BytecodeGenerator was capable of reasoning about the presence of with
statements, direct eval, or any other code features only within the current executable:

`
with (foo) {

(function() {

There was no way to detect WithScope during generation of this function.

})();

}
`

This change is required for op_to_this rewrite (#225397): if FunctionCallResolveNode and
friends knew there is no WithScope, op_call could be emitted with |this| value of
undefined as per spec [1], instead of resolved scope. This would:

  • simplify op_to_this on all tiers, likely resulting in minor perf boost;
  • save 1 instruction per strict function by removing op_to_this;
  • remove toThis() from the method table and ~30 its call sites from built-ins;
  • fix built-in methods that were observably lacking toThis();
  • fix proto getter / setter called on global scope;
  • fix WebIDL accessors called with |this| value of undefined and null.

Also, if ResolveNode knew that unforgeable global properties are not shadowed and there
is no with statement or sloppy mode direct eval, then undefined / Infinity / NaN
lookups could be constant-folded. This would save up to 3 bytecode ops per each usage
and allow emitting op_is_undefined_or_null for x === undefined || x === null.
V8 performs this optimization [2].

This patch introduces LexicalScopeFeatures to allow passing such information from Parser
to BytecodeGenerator with a minimal code diff. These features are kept separate from
CodeFeature to simplify reasoning about feature's scope and because we need to propagate
lexical features from parent to child scope.

Strict mode is the first use case of LexicalScopeFeatures, which this change carefully
fits into existing abstractions without increasing their memory usage even by 1 byte.

[1]: https://tc39.es/ecma262/#sec-evaluatecall (step 2)
[2]: https://medium.com/@bmeurer/sometimes-undefined-is-defined-7701e1c9eff8

  • builtins/BuiltinExecutables.cpp:

(JSC::BuiltinExecutables::createExecutable):

  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):

  • bytecode/UnlinkedCodeBlock.h:

(JSC::UnlinkedCodeBlock::recordParse):
(JSC::UnlinkedCodeBlock::lexicalScopeFeatures const):

  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::generateUnlinkedFunctionCodeBlock):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::UnlinkedFunctionExecutable::setInvalidTypeProfilingOffsets):

  • bytecode/UnlinkedFunctionExecutable.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitNewClassFieldInitializerFunction):

  • bytecompiler/BytecodeGenerator.h:

(JSC::BytecodeGenerator::lexicalScopeFeatures const):
(JSC::BytecodeGenerator::generate):

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createFunctionMetadata):

  • parser/Nodes.cpp:

(JSC::ScopeNode::ScopeNode):
(JSC::ProgramNode::ProgramNode):
(JSC::ModuleProgramNode::ModuleProgramNode):
(JSC::EvalNode::EvalNode):
(JSC::FunctionMetadataNode::FunctionMetadataNode):
(JSC::FunctionMetadataNode::operator== const):
(JSC::FunctionMetadataNode::dump const):
(JSC::FunctionNode::FunctionNode):

  • parser/Nodes.h:

(JSC::ScopeNode::lexicalScopeFeatures):
(JSC::ScopeNode::isStrictMode const):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseGeneratorFunctionSourceElements):
(JSC::Parser<LexerType>::parseAsyncFunctionSourceElements):
(JSC::Parser<LexerType>::parseAsyncGeneratorFunctionSourceElements):
(JSC::Parser<LexerType>::parseFunctionBody):
(JSC::Parser<LexerType>::parseFunctionInfo):

  • parser/Parser.h:

(JSC::Scope::Scope):
(JSC::Scope::lexicalScopeFeatures const):
(JSC::Scope::setStrictMode):
(JSC::Scope::strictMode const):
(JSC::Scope::fillParametersForSourceProviderCache):
(JSC::Scope::restoreFromSourceProviderCache):
(JSC::Parser::pushScope):
(JSC::Parser::lexicalScopeFeatures):
(JSC::Parser<LexerType>::parse):

  • parser/ParserModes.h:
  • parser/SourceProviderCacheItem.h:

(JSC::SourceProviderCacheItem::lexicalScopeFeatures const):
(JSC::SourceProviderCacheItem::SourceProviderCacheItem):

  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createFunctionMetadata):

  • runtime/CachedBytecode.cpp:

(JSC::CachedBytecode::addFunctionUpdate):

  • runtime/CachedTypes.cpp:

(JSC::CachedFunctionExecutable::lexicalScopeFeatures const):
(JSC::CachedCodeBlock::lexicalScopeFeatures const):
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
(JSC::CachedFunctionExecutable::encode):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::CachedCodeBlock<CodeBlockType>::encode):
(JSC::CachedFunctionExecutable::isInStrictContext const): Deleted.

  • runtime/CachedTypes.h:
  • runtime/CodeCache.cpp:

(JSC::generateUnlinkedCodeBlockImpl):
(JSC::CodeCache::getUnlinkedGlobalCodeBlock):

  • runtime/ECMAMode.h:

(JSC::ECMAMode::fromBool):

  • runtime/FunctionExecutable.cpp:

(JSC::FunctionExecutable::FunctionExecutable):

  • runtime/GlobalExecutable.h:

(JSC::GlobalExecutable::recordParse):
(JSC::GlobalExecutable::GlobalExecutable):

  • runtime/ScriptExecutable.cpp:

(JSC::ScriptExecutable::ScriptExecutable):
(JSC::ScriptExecutable::newCodeBlockFor):
(JSC::ScriptExecutable::recordParse):

  • runtime/ScriptExecutable.h:

(JSC::ScriptExecutable::isInStrictContext const):
(JSC::ScriptExecutable::recordParse):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/Parser.h

    r278253 r278588  
    148148
    149149public:
    150     Scope(const VM& vm, bool isFunction, bool isGenerator, bool strictMode, bool isArrowFunction, bool isAsyncFunction)
     150    Scope(const VM& vm, LexicalScopeFeatures lexicalScopeFeatures, bool isFunction, bool isGenerator, bool isArrowFunction, bool isAsyncFunction)
    151151        : m_vm(vm)
    152152        , m_shadowsArguments(false)
     
    157157        , m_allowsVarDeclarations(true)
    158158        , m_allowsLexicalDeclarations(true)
    159         , m_strictMode(strictMode)
     159        , m_lexicalScopeFeatures(lexicalScopeFeatures)
    160160        , m_isFunction(isFunction)
    161161        , m_isGenerator(isGenerator)
     
    757757        }
    758758    }
    759     void setStrictMode() { m_strictMode = true; }
    760     bool strictMode() const { return m_strictMode; }
     759    LexicalScopeFeatures lexicalScopeFeatures() const { return m_lexicalScopeFeatures; }
     760    void setStrictMode() { m_lexicalScopeFeatures |= StrictModeLexicalFeature; }
     761    bool strictMode() const { return m_lexicalScopeFeatures & StrictModeLexicalFeature; }
    761762    bool isValidStrictMode() const { return m_isValidStrictMode; }
    762763    bool shadowsArguments() const { return m_shadowsArguments; }
     
    781782        ASSERT(m_isFunction);
    782783        parameters.usesEval = m_usesEval;
    783         parameters.strictMode = m_strictMode;
     784        parameters.lexicalScopeFeatures = m_lexicalScopeFeatures;
    784785        parameters.needsFullActivation = m_needsFullActivation;
    785786        parameters.innerArrowFunctionFeatures = m_innerArrowFunctionFeatures;
     
    805806        ASSERT(m_isFunction);
    806807        m_usesEval = info->usesEval;
    807         m_strictMode = info->strictMode;
     808        m_lexicalScopeFeatures = info->lexicalScopeFeatures();
    808809        m_innerArrowFunctionFeatures = info->innerArrowFunctionFeatures;
    809810        m_needsFullActivation = info->needsFullActivation;
     
    905906    bool m_allowsVarDeclarations;
    906907    bool m_allowsLexicalDeclarations;
    907     bool m_strictMode;
     908    LexicalScopeFeatures m_lexicalScopeFeatures;
    908909    bool m_isFunction;
    909910    bool m_isGenerator;
     
    13261327    ScopeRef pushScope()
    13271328    {
     1329        LexicalScopeFeatures lexicalScopeFeatures = NoLexicalFeatures;
    13281330        bool isFunction = false;
    1329         bool isStrict = false;
    13301331        bool isGenerator = false;
    13311332        bool isArrowFunction = false;
    13321333        bool isAsyncFunction = false;
    13331334        if (!m_scopeStack.isEmpty()) {
    1334             isStrict = m_scopeStack.last().strictMode();
     1335            lexicalScopeFeatures = m_scopeStack.last().lexicalScopeFeatures();
    13351336            isFunction = m_scopeStack.last().isFunction();
    13361337            isGenerator = m_scopeStack.last().isGenerator();
     
    13381339            isAsyncFunction = m_scopeStack.last().isAsyncFunction();
    13391340        }
    1340         m_scopeStack.constructAndAppend(m_vm, isFunction, isGenerator, isStrict, isArrowFunction, isAsyncFunction);
     1341        m_scopeStack.constructAndAppend(m_vm, lexicalScopeFeatures, isFunction, isGenerator, isArrowFunction, isAsyncFunction);
    13411342        return currentScope();
    13421343    }
     
    16721673    void startSwitch() { currentScope()->startSwitch(); }
    16731674    void endSwitch() { currentScope()->endSwitch(); }
     1675    LexicalScopeFeatures lexicalScopeFeatures() { return currentScope()->lexicalScopeFeatures(); }
    16741676    void setStrictMode() { currentScope()->setStrictMode(); }
    16751677    bool strictMode() { return currentScope()->strictMode(); }
     
    21782180                                    *m_source,
    21792181                                    parseResult.value().features,
     2182                                    currentScope()->lexicalScopeFeatures(),
    21802183                                    currentScope()->innerArrowFunctionFeatures(),
    21812184                                    parseResult.value().numConstants,
Note: See TracChangeset for help on using the changeset viewer.