From 1414c7b049c5a8858f400a5e677cd20894463b32 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Sun, 13 Jun 2021 20:40:20 +0430 Subject: LibJS: Add a basic pass manager and add some basic passes This commit adds a bunch of passes, the most interesting of which is a pass that merges blocks together, and a pass that places blocks that flow into each other next to each other, and a very simply pass that removes duplicate basic blocks. Note that this does not remove the jump at the end of each block in that pass to avoid scope creep in the passes. --- Meta/Lagom/CMakeLists.txt | 3 +- Userland/Libraries/LibJS/Bytecode/BasicBlock.cpp | 1 + Userland/Libraries/LibJS/Bytecode/Instruction.h | 2 + Userland/Libraries/LibJS/Bytecode/Interpreter.cpp | 34 +++++ Userland/Libraries/LibJS/Bytecode/Interpreter.h | 9 ++ Userland/Libraries/LibJS/Bytecode/Label.h | 6 +- Userland/Libraries/LibJS/Bytecode/Op.cpp | 30 ++++ Userland/Libraries/LibJS/Bytecode/Op.h | 127 ++++++++++++---- Userland/Libraries/LibJS/Bytecode/Pass/DumpCFG.cpp | 27 ++++ .../Libraries/LibJS/Bytecode/Pass/GenerateCFG.cpp | 102 +++++++++++++ .../Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp | 165 +++++++++++++++++++++ .../Libraries/LibJS/Bytecode/Pass/PlaceBlocks.cpp | 54 +++++++ .../LibJS/Bytecode/Pass/UnifySameBlocks.cpp | 62 ++++++++ Userland/Libraries/LibJS/Bytecode/PassManager.h | 141 ++++++++++++++++++ Userland/Libraries/LibJS/CMakeLists.txt | 5 + .../Libraries/LibJS/Runtime/ScriptFunction.cpp | 4 + Userland/Utilities/js.cpp | 9 ++ 17 files changed, 751 insertions(+), 30 deletions(-) create mode 100644 Userland/Libraries/LibJS/Bytecode/Pass/DumpCFG.cpp create mode 100644 Userland/Libraries/LibJS/Bytecode/Pass/GenerateCFG.cpp create mode 100644 Userland/Libraries/LibJS/Bytecode/Pass/MergeBlocks.cpp create mode 100644 Userland/Libraries/LibJS/Bytecode/Pass/PlaceBlocks.cpp create mode 100644 Userland/Libraries/LibJS/Bytecode/Pass/UnifySameBlocks.cpp create mode 100644 Userland/Libraries/LibJS/Bytecode/PassManager.h diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 730d2bb549..1a350fdcbd 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -69,6 +69,7 @@ file(GLOB LIBMARKDOWN_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibMar file(GLOB LIBX86_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibX86/*.cpp") file(GLOB LIBJS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibJS/*.cpp") file(GLOB LIBJS_SUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibJS/*/*.cpp") +file(GLOB LIBJS_SUBSUBDIR_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibJS/*/*/*.cpp") file(GLOB LIBCOMPRESS_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCompress/*.cpp") file(GLOB LIBCOMPRESS_TESTS CONFIGURE_DEPENDS "../../Tests/LibCompress/*.cpp") file(GLOB LIBCRYPTO_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibCrypto/*.cpp") @@ -92,7 +93,7 @@ file(GLOB LIBTEST_MAIN CONFIGURE_DEPENDS "../../Userland/Libraries/LibTest/TestM set(LAGOM_REGEX_SOURCES ${LIBREGEX_LIBC_SOURCES} ${LIBREGEX_SOURCES}) set(LAGOM_CORE_SOURCES ${AK_SOURCES} ${LIBCORE_SOURCES}) -set(LAGOM_MORE_SOURCES ${LIBARCHIVE_SOURCES} ${LIBAUDIO_SOURCES} ${LIBELF_SOURCES} ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBCRYPTO_SUBSUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBTTF_SOURCES} ${LIBTEXTCODEC_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES} ${LIBGUI_GML_SOURCES} ${LIBHTTP_SOURCES} ${LAGOM_REGEX_SOURCES} ${SHELL_SOURCES} ${LIBSQL_SOURCES} ${LIBWASM_SOURCES} ${LIBIMAP_SOURCES}) +set(LAGOM_MORE_SOURCES ${LIBARCHIVE_SOURCES} ${LIBAUDIO_SOURCES} ${LIBELF_SOURCES} ${LIBIPC_SOURCES} ${LIBLINE_SOURCES} ${LIBJS_SOURCES} ${LIBJS_SUBDIR_SOURCES} ${LIBJS_SUBSUBDIR_SOURCES} ${LIBX86_SOURCES} ${LIBCRYPTO_SOURCES} ${LIBCOMPRESS_SOURCES} ${LIBCRYPTO_SUBDIR_SOURCES} ${LIBCRYPTO_SUBSUBDIR_SOURCES} ${LIBTLS_SOURCES} ${LIBTTF_SOURCES} ${LIBTEXTCODEC_SOURCES} ${LIBMARKDOWN_SOURCES} ${LIBGEMINI_SOURCES} ${LIBGFX_SOURCES} ${LIBGUI_GML_SOURCES} ${LIBHTTP_SOURCES} ${LAGOM_REGEX_SOURCES} ${SHELL_SOURCES} ${LIBSQL_SOURCES} ${LIBWASM_SOURCES} ${LIBIMAP_SOURCES}) set(LAGOM_TEST_SOURCES ${LIBTEST_SOURCES}) # FIXME: This is a hack, because the lagom stuff can be build individually or diff --git a/Userland/Libraries/LibJS/Bytecode/BasicBlock.cpp b/Userland/Libraries/LibJS/Bytecode/BasicBlock.cpp index 0182d4412f..8bac7aa06a 100644 --- a/Userland/Libraries/LibJS/Bytecode/BasicBlock.cpp +++ b/Userland/Libraries/LibJS/Bytecode/BasicBlock.cpp @@ -66,6 +66,7 @@ void BasicBlock::grow(size_t additional_size) void InstructionStreamIterator::operator++() { + VERIFY(!at_end()); m_offset += dereference().length(); } diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 11c9b6b745..ac0e8de028 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -80,10 +80,12 @@ public: #undef __BYTECODE_OP }; + bool is_terminator() const; Type type() const { return m_type; } size_t length() const; String to_string(Bytecode::Executable const&) const; void execute(Bytecode::Interpreter&) const; + void replace_references(BasicBlock const&, BasicBlock const&); static void destroy(Instruction&); protected: diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index eb63d3f895..4d4441cd7d 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -165,4 +165,38 @@ void Interpreter::continue_pending_unwind(Label const& resume_label) jump(resume_label); } } + +AK::Array, static_cast>(Interpreter::OptimizationLevel::__Count)> Interpreter::s_optimization_pipelines {}; + +Bytecode::PassManager& Interpreter::optimization_pipeline(Interpreter::OptimizationLevel level) +{ + auto underlying_level = to_underlying(level); + VERIFY(underlying_level <= to_underlying(Interpreter::OptimizationLevel::__Count)); + auto& entry = s_optimization_pipelines[underlying_level]; + + if (entry) + return *entry; + + auto pm = make(); + if (level == OptimizationLevel::Default) { + pm->add(); + pm->add(); + pm->add(); + pm->add(); + pm->add(); + pm->add(); + pm->add(); + pm->add(); + pm->add(); + pm->add(); + } else { + VERIFY_NOT_REACHED(); + } + + auto& passes = *pm; + entry = move(pm); + + return passes; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index 26b2dc9078..0dda59a99b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -7,6 +7,7 @@ #pragma once #include "Generator.h" +#include "PassManager.h" #include #include #include @@ -60,9 +61,17 @@ public: Executable const& current_executable() { return *m_current_executable; } + enum class OptimizationLevel { + Default, + __Count, + }; + static Bytecode::PassManager& optimization_pipeline(OptimizationLevel = OptimizationLevel::Default); + private: RegisterWindow& registers() { return m_register_windows.last(); } + static AK::Array, static_cast>(Interpreter::OptimizationLevel::__Count)> s_optimization_pipelines; + VM& m_vm; GlobalObject& m_global_object; NonnullOwnPtrVector m_register_windows; diff --git a/Userland/Libraries/LibJS/Bytecode/Label.h b/Userland/Libraries/LibJS/Bytecode/Label.h index 8174463f6e..b1634b1d0d 100644 --- a/Userland/Libraries/LibJS/Bytecode/Label.h +++ b/Userland/Libraries/LibJS/Bytecode/Label.h @@ -14,14 +14,14 @@ namespace JS::Bytecode { class Label { public: explicit Label(BasicBlock const& block) - : m_block(block) + : m_block(&block) { } - auto& block() const { return m_block; } + auto& block() const { return *m_block; } private: - BasicBlock const& m_block; + BasicBlock const* m_block { nullptr }; }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 676267e4d2..69744b6c22 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -165,6 +165,14 @@ void Jump::execute_impl(Bytecode::Interpreter& interpreter) const interpreter.jump(*m_true_target); } +void Jump::replace_references_impl(BasicBlock const& from, BasicBlock const& to) +{ + if (m_true_target.has_value() && &m_true_target->block() == &from) + m_true_target = Label { to }; + if (m_false_target.has_value() && &m_false_target->block() == &from) + m_false_target = Label { to }; +} + void JumpConditional::execute_impl(Bytecode::Interpreter& interpreter) const { VERIFY(m_true_target.has_value()); @@ -261,6 +269,16 @@ void EnterUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const interpreter.jump(m_entry_point); } +void EnterUnwindContext::replace_references_impl(BasicBlock const& from, BasicBlock const& to) +{ + if (&m_entry_point.block() == &from) + m_entry_point = Label { to }; + if (m_handler_target.has_value() && &m_handler_target->block() == &from) + m_handler_target = Label { to }; + if (m_finalizer_target.has_value() && &m_finalizer_target->block() == &from) + m_finalizer_target = Label { to }; +} + void LeaveUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const { interpreter.leave_unwind_context(); @@ -271,6 +289,12 @@ void ContinuePendingUnwind::execute_impl(Bytecode::Interpreter& interpreter) con interpreter.continue_pending_unwind(m_resume_target); } +void ContinuePendingUnwind::replace_references_impl(BasicBlock const& from, BasicBlock const& to) +{ + if (&m_resume_target.block() == &from) + m_resume_target = Label { to }; +} + void PushLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { HashMap resolved_variables; @@ -292,6 +316,12 @@ void Yield::execute_impl(Bytecode::Interpreter& interpreter) const interpreter.do_return(object); } +void Yield::replace_references_impl(BasicBlock const& from, BasicBlock const& to) +{ + if (m_continuation_label.has_value() && &m_continuation_label->block() == &from) + m_continuation_label = Label { to }; +} + void GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const { if (auto* object = interpreter.reg(m_base).to_object(interpreter.global_object())) { diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 0ca7ad91ab..653b9d1889 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -29,6 +29,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Register m_src; @@ -44,6 +45,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Value m_value; @@ -59,6 +61,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Register m_dst; @@ -88,20 +91,21 @@ private: O(RightShift, right_shift) \ O(UnsignedRightShift, unsigned_right_shift) -#define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ - class OpTitleCase final : public Instruction { \ - public: \ - explicit OpTitleCase(Register lhs_reg) \ - : Instruction(Type::OpTitleCase) \ - , m_lhs_reg(lhs_reg) \ - { \ - } \ - \ - void execute_impl(Bytecode::Interpreter&) const; \ - String to_string_impl(Bytecode::Executable const&) const; \ - \ - private: \ - Register m_lhs_reg; \ +#define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ + class OpTitleCase final : public Instruction { \ + public: \ + explicit OpTitleCase(Register lhs_reg) \ + : Instruction(Type::OpTitleCase) \ + , m_lhs_reg(lhs_reg) \ + { \ + } \ + \ + void execute_impl(Bytecode::Interpreter&) const; \ + String to_string_impl(Bytecode::Executable const&) const; \ + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } \ + \ + private: \ + Register m_lhs_reg; \ }; JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP) @@ -114,16 +118,17 @@ JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP) O(UnaryMinus, unary_minus) \ O(Typeof, typeof_) -#define JS_DECLARE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \ - class OpTitleCase final : public Instruction { \ - public: \ - OpTitleCase() \ - : Instruction(Type::OpTitleCase) \ - { \ - } \ - \ - void execute_impl(Bytecode::Interpreter&) const; \ - String to_string_impl(Bytecode::Executable const&) const; \ +#define JS_DECLARE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \ + class OpTitleCase final : public Instruction { \ + public: \ + OpTitleCase() \ + : Instruction(Type::OpTitleCase) \ + { \ + } \ + \ + void execute_impl(Bytecode::Interpreter&) const; \ + String to_string_impl(Bytecode::Executable const&) const; \ + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } \ }; JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP) @@ -139,6 +144,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: StringTableIndex m_string; @@ -153,6 +159,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } }; class NewBigInt final : public Instruction { @@ -165,6 +172,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Crypto::SignedBigInteger m_bigint; @@ -183,8 +191,12 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } - size_t length_impl() const { return sizeof(*this) + sizeof(Register) * m_element_count; } + size_t length_impl() const + { + return sizeof(*this) + sizeof(Register) * m_element_count; + } private: size_t m_element_count { 0 }; @@ -201,6 +213,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Register m_lhs; @@ -216,6 +229,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: StringTableIndex m_identifier; @@ -231,6 +245,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: StringTableIndex m_identifier; @@ -246,6 +261,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: StringTableIndex m_property; @@ -262,6 +278,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Register m_base; @@ -278,6 +295,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Register m_base; @@ -294,6 +312,7 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } private: Register m_base; @@ -326,6 +345,10 @@ public: void execute_impl(Bytecode::Interpreter&) const; String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&); + + auto& true_target() const { return m_true_target; } + auto& false_target() const { return m_false_target; } protected: Optional