Skip to content

Commit d79438a

Browse files
kalenikaliaksandrawesomekling
authored andcommitted
LibJS: Join locals, constants and registers into single vector
Merging registers, constants and locals into single vector means: - Better data locality - No need to check type in Interpreter::get() and Interpreter::set() which are very hot functions Performance improvement is visible in almost all Octane and Kraken tests.
1 parent 59cb799 commit d79438a

File tree

11 files changed

+478
-57
lines changed

11 files changed

+478
-57
lines changed

Userland/Libraries/LibJS/AST.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,10 @@ void ScopeNode::block_declaration_instantiation(VM& vm, Environment* environment
16241624

16251625
// iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.
16261626
if (function_declaration.name_identifier()->is_local()) {
1627-
vm.running_execution_context().local(function_declaration.name_identifier()->local_variable_index()) = function;
1627+
auto& running_execution_context = vm.running_execution_context();
1628+
auto number_of_registers = running_execution_context.executable->number_of_registers;
1629+
auto number_of_constants = running_execution_context.executable->constants.size();
1630+
running_execution_context.local(function_declaration.name_identifier()->local_variable_index() + number_of_registers + number_of_constants) = function;
16281631
} else {
16291632
VERIFY(is<DeclarativeEnvironment>(*environment));
16301633
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, vm, function_declaration.name(), function);

Userland/Libraries/LibJS/Bytecode/Generator.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,19 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_by
266266

267267
HashMap<size_t, SourceRecord> source_map;
268268

269+
for (auto& block : generator.m_root_basic_blocks) {
270+
if (!block->is_terminated()) {
271+
// NOTE: We must ensure that the "undefined" constant, which will be used by the not yet
272+
// emitted End instruction, is taken into account while shifting local operands by the
273+
// number of constants.
274+
(void)generator.add_constant(js_undefined());
275+
break;
276+
}
277+
}
278+
279+
auto number_of_registers = generator.m_next_register;
280+
auto number_of_constants = generator.m_constants.size();
281+
269282
for (auto& block : generator.m_root_basic_blocks) {
270283
basic_block_start_offsets.append(bytecode.size());
271284
if (block->handler() || block->finalizer()) {
@@ -287,6 +300,21 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_by
287300
while (!it.at_end()) {
288301
auto& instruction = const_cast<Instruction&>(*it);
289302

303+
instruction.visit_operands([number_of_registers, number_of_constants](Operand& operand) {
304+
switch (operand.type()) {
305+
case Operand::Type::Register:
306+
break;
307+
case Operand::Type::Local:
308+
operand.offset_index_by(number_of_registers + number_of_constants);
309+
break;
310+
case Operand::Type::Constant:
311+
operand.offset_index_by(number_of_registers);
312+
break;
313+
default:
314+
VERIFY_NOT_REACHED();
315+
}
316+
});
317+
290318
// OPTIMIZATION: Don't emit jumps that just jump to the next block.
291319
if (instruction.type() == Instruction::Type::Jump) {
292320
auto& jump = static_cast<Bytecode::Op::Jump&>(instruction);
@@ -333,6 +361,20 @@ CodeGenerationErrorOr<NonnullGCPtr<Executable>> Generator::emit_function_body_by
333361
}
334362
if (!block->is_terminated()) {
335363
Op::End end(generator.add_constant(js_undefined()));
364+
end.visit_operands([number_of_registers, number_of_constants](Operand& operand) {
365+
switch (operand.type()) {
366+
case Operand::Type::Register:
367+
break;
368+
case Operand::Type::Local:
369+
operand.offset_index_by(number_of_registers + number_of_constants);
370+
break;
371+
case Operand::Type::Constant:
372+
operand.offset_index_by(number_of_registers);
373+
break;
374+
default:
375+
VERIFY_NOT_REACHED();
376+
}
377+
});
336378
bytecode.append(reinterpret_cast<u8 const*>(&end), end.length());
337379
}
338380
if (block->handler() || block->finalizer()) {

Userland/Libraries/LibJS/Bytecode/Instruction.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ void Instruction::visit_labels(Function<void(JS::Bytecode::Label&)> visitor)
4242
#undef __BYTECODE_OP
4343
}
4444

45+
void Instruction::visit_operands(Function<void(JS::Bytecode::Operand&)> visitor)
46+
{
47+
#define __BYTECODE_OP(op) \
48+
case Type::op: \
49+
static_cast<Op::op&>(*this).visit_operands_impl(move(visitor)); \
50+
return;
51+
52+
switch (type()) {
53+
ENUMERATE_BYTECODE_OPS(__BYTECODE_OP)
54+
default:
55+
VERIFY_NOT_REACHED();
56+
}
57+
58+
#undef __BYTECODE_OP
59+
}
60+
4561
template<typename Op>
4662
concept HasVariableLength = Op::IsVariableLength;
4763

Userland/Libraries/LibJS/Bytecode/Instruction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class alignas(void*) Instruction {
157157
size_t length() const;
158158
ByteString to_byte_string(Bytecode::Executable const&) const;
159159
void visit_labels(Function<void(Label&)> visitor);
160+
void visit_operands(Function<void(Operand&)> visitor);
160161
static void destroy(Instruction&);
161162

162163
protected:
@@ -166,6 +167,7 @@ class alignas(void*) Instruction {
166167
}
167168

168169
void visit_labels_impl(Function<void(Label&)>) { }
170+
void visit_operands_impl(Function<void(Operand&)>) { }
169171

170172
private:
171173
Type m_type {};

Userland/Libraries/LibJS/Bytecode/Interpreter.cpp

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe
5454
break;
5555
case Operand::Type::Constant: {
5656
builder.append("\033[36m"sv);
57-
auto value = executable.constants[operand.index()];
57+
auto value = executable.constants[operand.index() - executable.number_of_registers];
5858
if (value.is_empty())
5959
builder.append("<Empty>"sv);
6060
else if (value.is_boolean())
@@ -153,30 +153,12 @@ Interpreter::~Interpreter()
153153

154154
ALWAYS_INLINE Value Interpreter::get(Operand op) const
155155
{
156-
switch (op.type()) {
157-
case Operand::Type::Register:
158-
return m_registers.data()[op.index()];
159-
case Operand::Type::Local:
160-
return m_locals.data()[op.index()];
161-
case Operand::Type::Constant:
162-
return m_constants.data()[op.index()];
163-
}
164-
__builtin_unreachable();
156+
return m_registers_and_constants_and_locals.data()[op.index()];
165157
}
166158

167159
ALWAYS_INLINE void Interpreter::set(Operand op, Value value)
168160
{
169-
switch (op.type()) {
170-
case Operand::Type::Register:
171-
m_registers.data()[op.index()] = value;
172-
return;
173-
case Operand::Type::Local:
174-
m_locals.data()[op.index()] = value;
175-
return;
176-
case Operand::Type::Constant:
177-
break;
178-
}
179-
__builtin_unreachable();
161+
m_registers_and_constants_and_locals.data()[op.index()] = value;
180162
}
181163

182164
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
@@ -346,7 +328,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
346328

347329
auto& running_execution_context = this->running_execution_context();
348330
auto* arguments = running_execution_context.arguments.data();
349-
auto* locals = running_execution_context.locals.data();
331+
auto* registers_and_constants_and_locals = running_execution_context.registers_and_constants_and_locals.data();
350332
auto& accumulator = this->accumulator();
351333
auto& executable = current_executable();
352334
auto const* bytecode = executable.bytecode.data();
@@ -384,7 +366,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point)
384366

385367
handle_SetLocal: {
386368
auto& instruction = *reinterpret_cast<Op::SetLocal const*>(&bytecode[program_counter]);
387-
locals[instruction.index()] = get(instruction.src());
369+
registers_and_constants_and_locals[instruction.index()] = get(instruction.src());
388370
DISPATCH_NEXT(SetLocal);
389371
}
390372

@@ -715,31 +697,35 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
715697
VERIFY(!vm().execution_context_stack().is_empty());
716698

717699
auto& running_execution_context = vm().running_execution_context();
718-
if (running_execution_context.registers.size() < executable.number_of_registers)
719-
running_execution_context.registers.resize(executable.number_of_registers);
700+
u32 registers_and_contants_count = executable.number_of_registers + executable.constants.size();
701+
if (running_execution_context.registers_and_constants_and_locals.size() < registers_and_contants_count)
702+
running_execution_context.registers_and_constants_and_locals.resize(registers_and_contants_count);
720703

721704
TemporaryChange restore_running_execution_context { m_running_execution_context, &running_execution_context };
722705
TemporaryChange restore_arguments { m_arguments, running_execution_context.arguments.span() };
723-
TemporaryChange restore_registers { m_registers, running_execution_context.registers.span() };
724-
TemporaryChange restore_locals { m_locals, running_execution_context.locals.span() };
725-
TemporaryChange restore_constants { m_constants, executable.constants.span() };
706+
TemporaryChange restore_registers_and_constants_and_locals { m_registers_and_constants_and_locals, running_execution_context.registers_and_constants_and_locals.span() };
726707

727708
reg(Register::accumulator()) = initial_accumulator_value;
728709
reg(Register::return_value()) = {};
729710

730711
running_execution_context.executable = &executable;
731712

713+
for (size_t i = 0; i < executable.constants.size(); ++i) {
714+
running_execution_context.registers_and_constants_and_locals[executable.number_of_registers + i] = executable.constants[i];
715+
}
716+
732717
run_bytecode(entry_point.value_or(0));
733718

734719
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter did run unit {:p}", &executable);
735720

736721
if constexpr (JS_BYTECODE_DEBUG) {
737-
for (size_t i = 0; i < registers().size(); ++i) {
722+
auto const& registers_and_constants_and_locals = running_execution_context.registers_and_constants_and_locals;
723+
for (size_t i = 0; i < executable.number_of_registers; ++i) {
738724
String value_string;
739-
if (registers()[i].is_empty())
725+
if (registers_and_constants_and_locals[i].is_empty())
740726
value_string = "(empty)"_string;
741727
else
742-
value_string = registers()[i].to_string_without_side_effects();
728+
value_string = registers_and_constants_and_locals[i].to_string_without_side_effects();
743729
dbgln("[{:3}] {}", i, value_string);
744730
}
745731
}
@@ -758,8 +744,8 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
758744
vm().finish_execution_generation();
759745

760746
if (!exception.is_empty())
761-
return { throw_completion(exception), running_execution_context.registers[0] };
762-
return { return_value, running_execution_context.registers[0] };
747+
return { throw_completion(exception), running_execution_context.registers_and_constants_and_locals[0] };
748+
return { return_value, running_execution_context.registers_and_constants_and_locals[0] };
763749
}
764750

765751
void Interpreter::enter_unwind_context()

Userland/Libraries/LibJS/Bytecode/Interpreter.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ class Interpreter {
4949
ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); }
5050
Value& reg(Register const& r)
5151
{
52-
return m_registers.data()[r.index()];
52+
return m_registers_and_constants_and_locals.data()[r.index()];
5353
}
5454
Value reg(Register const& r) const
5555
{
56-
return m_registers.data()[r.index()];
56+
return m_registers_and_constants_and_locals.data()[r.index()];
5757
}
5858

5959
[[nodiscard]] Value get(Operand) const;
@@ -77,9 +77,6 @@ class Interpreter {
7777
Executable const& current_executable() const { return *m_current_executable; }
7878
Optional<size_t> program_counter() const { return m_program_counter; }
7979

80-
Vector<Value>& registers() { return vm().running_execution_context().registers; }
81-
Vector<Value> const& registers() const { return vm().running_execution_context().registers; }
82-
8380
ExecutionContext& running_execution_context() { return *m_running_execution_context; }
8481

8582
private:
@@ -99,9 +96,7 @@ class Interpreter {
9996
GCPtr<DeclarativeEnvironment> m_global_declarative_environment { nullptr };
10097
Optional<size_t&> m_program_counter;
10198
Span<Value> m_arguments;
102-
Span<Value> m_registers;
103-
Span<Value> m_locals;
104-
Span<Value> m_constants;
99+
Span<Value> m_registers_and_constants_and_locals;
105100
ExecutionContext* m_running_execution_context { nullptr };
106101
};
107102

0 commit comments

Comments
 (0)