浏览代码

LibJS/Bytecode: Don't create empty lexical environments

This patch stops emitting the BlockDeclarationInstantiation instruction
when there are no locals, and no function declarations in the scope.

We were spending 20% of CPU time on https://ventrella.com/Clusters/ just
creating empty environments for no reason.
Andreas Kling 1 年之前
父节点
当前提交
b5a070e8ce

+ 5 - 6
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -44,8 +44,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ScopeNode::generate_byt
 
     if (is<BlockStatement>(*this)) {
         if (has_lexical_declarations()) {
-            generator.block_declaration_instantiation(*this);
-            did_create_lexical_environment = true;
+            did_create_lexical_environment = generator.emit_block_declaration_instantiation(*this);
         }
     } else if (is<Program>(*this)) {
         // GlobalDeclarationInstantiation is handled by the C++ AO.
@@ -2655,9 +2654,9 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> SwitchStatement::genera
     Bytecode::BasicBlock* entry_block_for_default { nullptr };
     Bytecode::BasicBlock* next_test_block = &generator.make_block();
 
-    auto has_lexical_declarations = this->has_lexical_declarations();
-    if (has_lexical_declarations)
-        generator.block_declaration_instantiation(*this);
+    bool did_create_lexical_environment = false;
+    if (has_lexical_declarations())
+        did_create_lexical_environment = generator.emit_block_declaration_instantiation(*this);
 
     generator.emit<Bytecode::Op::Jump>(Bytecode::Label { *next_test_block });
 
@@ -2721,7 +2720,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> SwitchStatement::genera
 
     generator.switch_to_basic_block(end_block);
 
-    if (has_lexical_declarations)
+    if (did_create_lexical_environment)
         generator.end_variable_scope();
 
     return dst;

+ 19 - 1
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -448,10 +448,28 @@ Label Generator::nearest_continuable_scope() const
     return m_continuable_scopes.last().bytecode_target;
 }
 
-void Generator::block_declaration_instantiation(ScopeNode const& scope_node)
+bool Generator::emit_block_declaration_instantiation(ScopeNode const& scope_node)
 {
+    bool needs_block_declaration_instantiation = false;
+    MUST(scope_node.for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
+        if (declaration.is_function_declaration()) {
+            needs_block_declaration_instantiation = true;
+            return;
+        }
+        MUST(declaration.for_each_bound_identifier([&](auto const& id) {
+            if (!id.is_local())
+                needs_block_declaration_instantiation = true;
+        }));
+    }));
+
+    if (!needs_block_declaration_instantiation)
+        return false;
+
+    // FIXME: Generate the actual bytecode for block declaration instantiation
+    //        and get rid of the BlockDeclarationInstantiation instruction.
     start_boundary(BlockBoundaryType::LeaveLexicalEnvironment);
     emit<Bytecode::Op::BlockDeclarationInstantiation>(scope_node);
+    return true;
 }
 
 void Generator::begin_variable_scope()

+ 2 - 1
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -213,7 +213,8 @@ public:
         SurroundingScopeKind kind;
     };
 
-    void block_declaration_instantiation(ScopeNode const&);
+    // Returns true if a lexical environment was created.
+    bool emit_block_declaration_instantiation(ScopeNode const&);
 
     void begin_variable_scope();
     void end_variable_scope();