Sfoglia il codice sorgente

LibJS: Automatically split linear bytecode into multiple blocks

...instead of crashing :^)
Ali Mohammad Pur 4 anni fa
parent
commit
4cfdfb6a88

+ 3 - 0
Userland/Libraries/LibJS/Bytecode/BasicBlock.h

@@ -44,6 +44,8 @@ struct UnwindInfo {
 };
 
 class BasicBlock {
+    AK_MAKE_NONCOPYABLE(BasicBlock);
+
 public:
     static NonnullOwnPtr<BasicBlock> create(String name);
     ~BasicBlock();
@@ -54,6 +56,7 @@ public:
     ReadonlyBytes instruction_stream() const { return ReadonlyBytes { m_buffer, m_buffer_size }; }
 
     void* next_slot() { return m_buffer + m_buffer_size; }
+    bool can_grow(size_t additional_size) const { return m_buffer_size + additional_size <= m_buffer_capacity; }
     void grow(size_t additional_size);
 
     void terminate(Badge<Generator>) { m_is_terminated = true; }

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

@@ -8,6 +8,7 @@
 #include <LibJS/Bytecode/BasicBlock.h>
 #include <LibJS/Bytecode/Generator.h>
 #include <LibJS/Bytecode/Instruction.h>
+#include <LibJS/Bytecode/Op.h>
 #include <LibJS/Bytecode/Register.h>
 #include <LibJS/Forward.h>
 

+ 21 - 0
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -11,6 +11,7 @@
 #include <AK/SinglyLinkedList.h>
 #include <LibJS/Bytecode/BasicBlock.h>
 #include <LibJS/Bytecode/Label.h>
+#include <LibJS/Bytecode/Op.h>
 #include <LibJS/Bytecode/Register.h>
 #include <LibJS/Bytecode/StringTable.h>
 #include <LibJS/Forward.h>
@@ -31,10 +32,26 @@ public:
 
     Register allocate_register();
 
+    void ensure_enough_space(size_t size)
+    {
+        // Make sure there's always enough space for a single jump at the end.
+        if (!m_current_basic_block->can_grow(size + sizeof(Op::Jump))) {
+            auto& new_block = make_block();
+            emit<Op::Jump>().set_targets(
+                Label { new_block },
+                {});
+            switch_to_basic_block(new_block);
+        }
+    }
+
     template<typename OpType, typename... Args>
     OpType& emit(Args&&... args)
     {
         VERIFY(!is_current_block_terminated());
+        // If the block doesn't have enough space, switch to another block
+        if constexpr (!OpType::IsTerminator)
+            ensure_enough_space(sizeof(OpType));
+
         void* slot = next_slot();
         grow(sizeof(OpType));
         new (slot) OpType(forward<Args>(args)...);
@@ -47,6 +64,10 @@ public:
     OpType& emit_with_extra_register_slots(size_t extra_register_slots, Args&&... args)
     {
         VERIFY(!is_current_block_terminated());
+        // If the block doesn't have enough space, switch to another block
+        if constexpr (!OpType::IsTerminator)
+            ensure_enough_space(sizeof(OpType) + extra_register_slots * sizeof(Register));
+
         void* slot = next_slot();
         grow(sizeof(OpType) + extra_register_slots * sizeof(Register));
         new (slot) OpType(forward<Args>(args)...);