blob: 49b34e138ad5f9604be786bd2c8216b028b85fdb (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtrVector.h>
#include <AK/OwnPtr.h>
#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>
namespace JS::Bytecode {
struct Executable {
NonnullOwnPtrVector<BasicBlock> basic_blocks;
NonnullOwnPtr<StringTable> string_table;
size_t number_of_registers { 0 };
String const& get_string(StringTableIndex index) const { return string_table->get(index); }
};
class Generator {
public:
static Executable generate(ASTNode const&, bool is_in_generator_function = false);
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)...);
if constexpr (OpType::IsTerminator)
m_current_basic_block->terminate({});
return *static_cast<OpType*>(slot);
}
template<typename OpType, typename... Args>
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)...);
if constexpr (OpType::IsTerminator)
m_current_basic_block->terminate({});
return *static_cast<OpType*>(slot);
}
void begin_continuable_scope(Label continue_target);
void end_continuable_scope();
void begin_breakable_scope(Label breakable_target);
void end_breakable_scope();
[[nodiscard]] Label nearest_continuable_scope() const;
[[nodiscard]] Label nearest_breakable_scope() const;
void switch_to_basic_block(BasicBlock& block)
{
m_current_basic_block = █
}
[[nodiscard]] BasicBlock& current_block() { return *m_current_basic_block; }
BasicBlock& make_block(String name = {})
{
if (name.is_empty())
name = String::number(m_next_block++);
m_root_basic_blocks.append(BasicBlock::create(name));
return m_root_basic_blocks.last();
}
bool is_current_block_terminated() const
{
return m_current_basic_block->is_terminated();
}
StringTableIndex intern_string(StringView const& string)
{
return m_string_table->insert(string);
}
bool is_in_generator_function() const { return m_is_in_generator_function; }
void enter_generator_context() { m_is_in_generator_function = true; }
void leave_generator_context() { m_is_in_generator_function = false; }
private:
Generator();
~Generator();
void grow(size_t);
void* next_slot();
BasicBlock* m_current_basic_block { nullptr };
NonnullOwnPtrVector<BasicBlock> m_root_basic_blocks;
NonnullOwnPtr<StringTable> m_string_table;
u32 m_next_register { 2 };
u32 m_next_block { 1 };
bool m_is_in_generator_function { false };
Vector<Label> m_continuable_scopes;
Vector<Label> m_breakable_scopes;
};
}
|