From 926538580765653931d6df95d341256b75f57d68 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 14 May 2024 10:57:06 +0200 Subject: [PATCH] LibJS/Bytecode: Don't bother propagating completion values in functions The last completion value in a function is not exposed to the language, since functions always either return something, or undefined. Given this, we can avoid emitting code that propagates the completion value from various statements, as long as we know we're generating code for a context where the completion value is not accessible. In practical terms, this means that function code gets to do less completion shuffling, while global and eval code has to keep doing it. --- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 115 +++++++++++------- .../Libraries/LibJS/Bytecode/Generator.cpp | 9 +- Userland/Libraries/LibJS/Bytecode/Generator.h | 12 +- 3 files changed, 89 insertions(+), 47 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 5b986151a9b..f976b941183 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -800,8 +800,11 @@ Bytecode::CodeGenerationErrorOr> WhileStatement::generat auto& body_block = generator.make_block(); auto& end_block = generator.make_block(); - auto result = generator.allocate_register(); - generator.emit(result, generator.add_constant(js_undefined())); + Optional completion; + if (generator.must_propagate_completion()) { + completion = generator.allocate_register(); + generator.emit(*completion, generator.add_constant(js_undefined())); + } generator.emit(Bytecode::Label { test_block }); @@ -820,13 +823,15 @@ Bytecode::CodeGenerationErrorOr> WhileStatement::generat generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { - if (body.has_value()) - generator.emit(result, body.value()); + if (generator.must_propagate_completion()) { + if (body.has_value()) + generator.emit(*completion, body.value()); + } generator.emit(Bytecode::Label { test_block }); } generator.switch_to_basic_block(end_block); - return result; + return completion; } Bytecode::CodeGenerationErrorOr> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -849,8 +854,11 @@ Bytecode::CodeGenerationErrorOr> DoWhileStatement::gener auto& load_result_and_jump_to_end_block = generator.make_block(); auto& end_block = generator.make_block(); - auto completion_value = generator.allocate_register(); - generator.emit(completion_value, generator.add_constant(js_undefined())); + Optional completion; + if (generator.must_propagate_completion()) { + completion = generator.allocate_register(); + generator.emit(*completion, generator.add_constant(js_undefined())); + } // jump to the body block generator.emit(Bytecode::Label { body_block }); @@ -870,8 +878,10 @@ Bytecode::CodeGenerationErrorOr> DoWhileStatement::gener generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { - if (body_result.has_value()) - generator.emit(completion_value, body_result.value()); + if (generator.must_propagate_completion()) { + if (body_result.has_value()) + generator.emit(*completion, body_result.value()); + } generator.emit(Bytecode::Label { test_block }); } @@ -879,7 +889,7 @@ Bytecode::CodeGenerationErrorOr> DoWhileStatement::gener generator.emit(Bytecode::Label { end_block }); generator.switch_to_basic_block(end_block); - return completion_value; + return completion; } Bytecode::CodeGenerationErrorOr> ForStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -2246,8 +2256,11 @@ Bytecode::CodeGenerationErrorOr> IfStatement::generate_b auto& false_block = generator.make_block(); auto& end_block = generator.make_block(); - auto dst = choose_dst(generator, preferred_dst); - generator.emit(dst, generator.add_constant(js_undefined())); + Optional completion; + if (generator.must_propagate_completion()) { + completion = choose_dst(generator, preferred_dst); + generator.emit(*completion, generator.add_constant(js_undefined())); + } auto predicate = TRY(m_predicate->generate_bytecode(generator)).value(); generator.emit_jump_if( @@ -2256,10 +2269,12 @@ Bytecode::CodeGenerationErrorOr> IfStatement::generate_b Bytecode::Label { false_block }); generator.switch_to_basic_block(true_block); - auto consequent = TRY(m_consequent->generate_bytecode(generator, dst)); + auto consequent = TRY(m_consequent->generate_bytecode(generator, completion)); if (!generator.is_current_block_terminated()) { - if (consequent.has_value()) - generator.emit(dst, *consequent); + if (generator.must_propagate_completion()) { + if (consequent.has_value()) + generator.emit(*completion, *consequent); + } generator.emit(Bytecode::Label { end_block }); } @@ -2267,17 +2282,19 @@ Bytecode::CodeGenerationErrorOr> IfStatement::generate_b Optional alternate; if (m_alternate) { - alternate = TRY(m_alternate->generate_bytecode(generator, dst)); + alternate = TRY(m_alternate->generate_bytecode(generator, completion)); } if (!generator.is_current_block_terminated()) { - if (alternate.has_value()) - generator.emit(dst, *alternate); + if (generator.must_propagate_completion()) { + if (alternate.has_value()) + generator.emit(*completion, *alternate); + } generator.emit(Bytecode::Label { end_block }); } generator.switch_to_basic_block(end_block); - return dst; + return completion; } Bytecode::CodeGenerationErrorOr> ContinueStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -2508,7 +2525,7 @@ Bytecode::CodeGenerationErrorOr> TryStatement::generate_ Bytecode::BasicBlock* next_block { nullptr }; - Optional dst; + Optional completion; if (m_finalizer) { // FIXME: See notes in Op.h->ScheduleJump @@ -2564,9 +2581,11 @@ Bytecode::CodeGenerationErrorOr> TryStatement::generate_ })); auto handler_result = TRY(m_handler->body().generate_bytecode(generator)); - if (handler_result.has_value() && !generator.is_current_block_terminated()) { - dst = generator.allocate_register(); - generator.emit(*dst, *handler_result); + if (generator.must_propagate_completion()) { + if (handler_result.has_value() && !generator.is_current_block_terminated()) { + completion = generator.allocate_register(); + generator.emit(*completion, *handler_result); + } } handler_target = Bytecode::Label { handler_block }; @@ -2608,9 +2627,11 @@ Bytecode::CodeGenerationErrorOr> TryStatement::generate_ generator.switch_to_basic_block(target_block); auto block_result = TRY(m_block->generate_bytecode(generator)); if (!generator.is_current_block_terminated()) { - if (block_result.has_value()) { - dst = generator.allocate_register(); - generator.emit(*dst, *block_result); + if (generator.must_propagate_completion()) { + if (block_result.has_value()) { + completion = generator.allocate_register(); + generator.emit(*completion, *block_result); + } } if (m_finalizer) { @@ -2630,9 +2651,11 @@ Bytecode::CodeGenerationErrorOr> TryStatement::generate_ generator.end_boundary(Bytecode::Generator::BlockBoundaryType::Unwind); generator.switch_to_basic_block(next_block ? *next_block : saved_block); - if (!dst.has_value()) - return generator.add_constant(js_undefined()); - return dst; + if (generator.must_propagate_completion()) { + if (!completion.has_value()) + return generator.add_constant(js_undefined()); + } + return completion; } Bytecode::CodeGenerationErrorOr> SwitchStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const @@ -2645,8 +2668,11 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::genera { Bytecode::Generator::SourceLocationScope scope(generator, *this); - auto dst = generator.allocate_register(); - generator.emit(dst, generator.add_constant(js_undefined())); + Optional completion; + if (generator.must_propagate_completion()) { + completion = generator.allocate_register(); + generator.emit(*completion, generator.add_constant(js_undefined())); + } auto discriminant = TRY(m_discriminant->generate_bytecode(generator)).value(); Vector case_blocks; @@ -2699,10 +2725,12 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::genera auto result = TRY(statement->generate_bytecode(generator)); if (generator.is_current_block_terminated()) break; - if (result.has_value()) - generator.emit(dst, *result); - else - generator.emit(dst, generator.add_constant(js_undefined())); + if (generator.must_propagate_completion()) { + if (result.has_value()) + generator.emit(*completion, *result); + else + generator.emit(*completion, generator.add_constant(js_undefined())); + } } if (!generator.is_current_block_terminated()) { auto next_block = current_block; @@ -2722,7 +2750,7 @@ Bytecode::CodeGenerationErrorOr> SwitchStatement::genera if (did_create_lexical_environment) generator.end_variable_scope(); - return dst; + return completion; } Bytecode::CodeGenerationErrorOr> SuperExpression::generate_bytecode(Bytecode::Generator&, [[maybe_unused]] Optional preferred_dst) const @@ -3026,8 +3054,11 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_body_e bool has_lexical_binding = false; // 3. Let V be undefined. - auto completion_value = generator.allocate_register(); - generator.emit(completion_value, generator.add_constant(js_undefined())); + Optional completion; + if (generator.must_propagate_completion()) { + completion = generator.allocate_register(); + generator.emit(*completion, generator.add_constant(js_undefined())); + } // 4. Let destructuring be IsDestructuring of lhs. auto destructuring = head_result.is_destructuring; @@ -3229,14 +3260,16 @@ static Bytecode::CodeGenerationErrorOr> for_in_of_body_e // The body can contain an unconditional block terminator (e.g. return, throw), so we have to check for that before generating the Jump. if (!generator.is_current_block_terminated()) { - if (result.has_value()) - generator.emit(completion_value, *result); + if (generator.must_propagate_completion()) { + if (result.has_value()) + generator.emit(*completion, *result); + } generator.emit(Bytecode::Label { loop_update }); } generator.switch_to_basic_block(loop_end); - return completion_value; + return completion; } Bytecode::CodeGenerationErrorOr> ForInStatement::generate_bytecode(Bytecode::Generator& generator, [[maybe_unused]] Optional preferred_dst) const diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 513e9ab4774..1e1ba31a9bb 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -17,13 +17,14 @@ namespace JS::Bytecode { -Generator::Generator(VM& vm) +Generator::Generator(VM& vm, MustPropagateCompletion must_propagate_completion) : m_vm(vm) , m_string_table(make()) , m_identifier_table(make()) , m_regex_table(make()) , m_constants(vm.heap()) , m_accumulator(*this, Operand(Register::accumulator())) + , m_must_propagate_completion(must_propagate_completion == MustPropagateCompletion::Yes) { } @@ -190,9 +191,9 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E return {}; } -CodeGenerationErrorOr> Generator::emit_function_body_bytecode(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GCPtr function) +CodeGenerationErrorOr> Generator::emit_function_body_bytecode(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GCPtr function, MustPropagateCompletion must_propagate_completion) { - Generator generator(vm); + Generator generator(vm, must_propagate_completion); generator.switch_to_basic_block(generator.make_block()); SourceLocationScope scope(generator, node); @@ -429,7 +430,7 @@ CodeGenerationErrorOr> Generator::generate_from_ast_nod CodeGenerationErrorOr> Generator::generate_from_function(VM& vm, ECMAScriptFunctionObject const& function) { - return emit_function_body_bytecode(vm, function.ecmascript_code(), function.kind(), &function); + return emit_function_body_bytecode(vm, function.ecmascript_code(), function.kind(), &function, MustPropagateCompletion::No); } void Generator::grow(size_t additional_size) diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index 6b577e55174..f28cb4ba7e6 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -33,6 +33,11 @@ public: Block, }; + enum class MustPropagateCompletion { + No, + Yes, + }; + static CodeGenerationErrorOr> generate_from_ast_node(VM&, ASTNode const&, FunctionKind = FunctionKind::Normal); static CodeGenerationErrorOr> generate_from_function(VM&, ECMAScriptFunctionObject const& function); @@ -302,10 +307,12 @@ public: [[nodiscard]] bool is_finished() const { return m_finished; } + [[nodiscard]] bool must_propagate_completion() const { return m_must_propagate_completion; } + private: VM& m_vm; - static CodeGenerationErrorOr> emit_function_body_bytecode(VM&, ASTNode const&, FunctionKind, GCPtr); + static CodeGenerationErrorOr> emit_function_body_bytecode(VM&, ASTNode const&, FunctionKind, GCPtr, MustPropagateCompletion = MustPropagateCompletion::Yes); enum class JumpType { Continue, @@ -314,7 +321,7 @@ private: void generate_scoped_jump(JumpType); void generate_labelled_jump(JumpType, DeprecatedFlyString const& label); - explicit Generator(VM&); + Generator(VM&, MustPropagateCompletion); ~Generator() = default; void grow(size_t); @@ -353,6 +360,7 @@ private: HashTable m_initialized_locals; bool m_finished { false }; + bool m_must_propagate_completion { true }; }; }