mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-08 06:09:58 +00:00
LibJS: Generate C++ bytecode instruction classes from a definition file
This commit adds a new Bytecode.def file that describes all the LibJS bytecode instructions. From this, we are able to generate the full declarations for all C++ bytecode instruction classes, as well as their serialization code. Note that some of the bytecode compiler was updated since instructions no longer have default constructor arguments. The big immediate benefit here is that we lose a couple thousand lines of hand-written C++ code. Going forward, this also allows us to do more tooling for the bytecode VM, now that we have an authoritative description of its instructions. Key things to know about: - Instructions can inherit from one another. At the moment, everything simply inherits from the base "Instruction". - @terminator means the instruction terminates a basic block. - @nothrow means the instruction cannot throw. This affects how the interpreter interacts with it. - Variable-length instructions are automatically supported. Just put an array of something as the last field of the instruction. - The m_length field is magical. If present, it will be populated with the full length of the instruction. This is used for variable-length instructions.
This commit is contained in:
parent
84443e1de6
commit
003589db2d
Notes:
github-actions[bot]
2025-11-21 08:47:47 +00:00
Author: https://github.com/awesomekling
Commit: 003589db2d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6888
12 changed files with 1975 additions and 4198 deletions
|
|
@ -495,7 +495,7 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> arguments_to_arr
|
|||
{
|
||||
auto dst = generator.allocate_register();
|
||||
if (arguments.is_empty()) {
|
||||
generator.emit<Bytecode::Op::NewArray>(dst);
|
||||
generator.emit<Bytecode::Op::NewArray>(dst, ReadonlySpan<ScopedOperand> {});
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
|
@ -512,9 +512,9 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> arguments_to_arr
|
|||
}
|
||||
|
||||
if (first_spread.index() != 0)
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(args.size(), dst, args);
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(args.size(), dst, args.span());
|
||||
else
|
||||
generator.emit<Bytecode::Op::NewArray>(dst);
|
||||
generator.emit<Bytecode::Op::NewArray>(dst, ReadonlySpan<ScopedOperand> {});
|
||||
|
||||
if (first_spread != arguments.end()) {
|
||||
for (auto it = first_spread; it != arguments.end(); ++it) {
|
||||
|
|
@ -1034,7 +1034,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ForStatement::generate_
|
|||
if (identifier.is_local())
|
||||
return;
|
||||
auto index = generator.intern_identifier(identifier.string());
|
||||
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_const);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(index, Bytecode::Op::EnvironmentMode::Lexical, is_const, false, false);
|
||||
if (!is_const) {
|
||||
per_iteration_bindings.append(index);
|
||||
}
|
||||
|
|
@ -1067,7 +1067,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ForStatement::generate_
|
|||
generator.begin_variable_scope();
|
||||
|
||||
for (size_t i = 0; i < per_iteration_bindings.size(); ++i) {
|
||||
generator.emit<Bytecode::Op::CreateVariable>(per_iteration_bindings[i], Bytecode::Op::EnvironmentMode::Lexical, false);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(per_iteration_bindings[i], Bytecode::Op::EnvironmentMode::Lexical, false, false, false);
|
||||
generator.emit<Bytecode::Op::InitializeLexicalBinding>(per_iteration_bindings[i], registers[i]);
|
||||
}
|
||||
};
|
||||
|
|
@ -1206,7 +1206,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ArrayExpression::genera
|
|||
Bytecode::Generator::SourceLocationScope scope(generator, *this);
|
||||
if (m_elements.is_empty()) {
|
||||
auto dst = choose_dst(generator, preferred_dst);
|
||||
generator.emit<Bytecode::Op::NewArray>(dst);
|
||||
generator.emit<Bytecode::Op::NewArray>(dst, ReadonlySpan<ScopedOperand> {});
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
|
@ -1242,7 +1242,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ArrayExpression::genera
|
|||
if (first_spread.index() != 0) {
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(args.size(), dst, args);
|
||||
} else {
|
||||
generator.emit<Bytecode::Op::NewArray>(dst);
|
||||
generator.emit<Bytecode::Op::NewArray>(dst, ReadonlySpan<ScopedOperand> {});
|
||||
}
|
||||
|
||||
if (first_spread != m_elements.end()) {
|
||||
|
|
@ -1288,7 +1288,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> FunctionExpression::gen
|
|||
generator.begin_variable_scope();
|
||||
|
||||
name_identifier = generator.intern_identifier(name());
|
||||
generator.emit<Bytecode::Op::CreateVariable>(*name_identifier, Bytecode::Op::EnvironmentMode::Lexical, true);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(*name_identifier, Bytecode::Op::EnvironmentMode::Lexical, true, false, false);
|
||||
}
|
||||
|
||||
auto new_function = choose_dst(generator, preferred_dst);
|
||||
|
|
@ -1437,7 +1437,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_array_binding_pattern_byte
|
|||
auto iterator_object = generator.allocate_register();
|
||||
auto iterator_next_method = generator.allocate_register();
|
||||
auto iterator_done_property = generator.allocate_register();
|
||||
generator.emit<Bytecode::Op::GetIterator>(iterator_object, iterator_next_method, iterator_done_property, input_array);
|
||||
generator.emit<Bytecode::Op::GetIterator>(iterator_object, iterator_next_method, iterator_done_property, input_array, IteratorHint::Sync);
|
||||
bool first = true;
|
||||
|
||||
auto assign_value_to_alias = [&](auto& alias, ScopedOperand value) {
|
||||
|
|
@ -1485,7 +1485,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_array_binding_pattern_byte
|
|||
value = generator.allocate_register();
|
||||
|
||||
generator.switch_to_basic_block(if_exhausted_block);
|
||||
generator.emit<Bytecode::Op::NewArray>(value);
|
||||
generator.emit<Bytecode::Op::NewArray>(value, ReadonlySpan<ScopedOperand> {});
|
||||
generator.emit<Bytecode::Op::Jump>(Bytecode::Label { continuation_block });
|
||||
|
||||
generator.switch_to_basic_block(if_not_exhausted_block);
|
||||
|
|
@ -1801,32 +1801,32 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> CallExpression::generat
|
|||
dst,
|
||||
callee,
|
||||
this_value,
|
||||
argument_operands,
|
||||
builtin.value(),
|
||||
expression_string_index);
|
||||
expression_string_index,
|
||||
argument_operands);
|
||||
} else if (call_type == Op::CallType::Construct) {
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::CallConstruct>(
|
||||
argument_operands.size(),
|
||||
dst,
|
||||
callee,
|
||||
argument_operands,
|
||||
expression_string_index);
|
||||
expression_string_index,
|
||||
argument_operands);
|
||||
} else if (call_type == Op::CallType::DirectEval) {
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::CallDirectEval>(
|
||||
argument_operands.size(),
|
||||
dst,
|
||||
callee,
|
||||
this_value,
|
||||
argument_operands,
|
||||
expression_string_index);
|
||||
expression_string_index,
|
||||
argument_operands);
|
||||
} else {
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::Call>(
|
||||
argument_operands.size(),
|
||||
dst,
|
||||
callee,
|
||||
this_value,
|
||||
argument_operands,
|
||||
expression_string_index);
|
||||
expression_string_index,
|
||||
argument_operands);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2023,7 +2023,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
|||
auto array = generator.allocate_register();
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(1, array, ReadonlySpan<ScopedOperand> { &received_completion_value, 1 });
|
||||
auto inner_result = generator.allocate_register();
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(inner_result, next_method, iterator, array);
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(inner_result, next_method, iterator, array, OptionalNone {});
|
||||
|
||||
// ii. If generatorKind is async, set innerResult to ? Await(innerResult).
|
||||
if (generator.is_in_async_generator_function()) {
|
||||
|
|
@ -2112,7 +2112,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
|||
// 1. Let innerResult be ? Call(throw, iterator, « received.[[Value]] »).
|
||||
auto received_value_array = generator.allocate_register();
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(1, received_value_array, ReadonlySpan<ScopedOperand> { &received_completion_value, 1 });
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(inner_result, throw_method, iterator, received_value_array);
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(inner_result, throw_method, iterator, received_value_array, OptionalNone {});
|
||||
|
||||
// 2. If generatorKind is async, set innerResult to ? Await(innerResult).
|
||||
if (generator.is_in_async_generator_function()) {
|
||||
|
|
@ -2209,7 +2209,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> YieldExpression::genera
|
|||
auto call_array = generator.allocate_register();
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(1, call_array, ReadonlySpan<ScopedOperand> { &received_completion_value, 1 });
|
||||
auto inner_return_result = generator.allocate_register();
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(inner_return_result, return_method, iterator, call_array);
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(inner_return_result, return_method, iterator, call_array, OptionalNone {});
|
||||
|
||||
// v. If generatorKind is async, set innerReturnResult to ? Await(innerReturnResult).
|
||||
if (generator.is_in_async_generator_function()) {
|
||||
|
|
@ -2508,7 +2508,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TaggedTemplateLiteral::
|
|||
|
||||
auto strings_array = generator.allocate_register();
|
||||
if (string_regs.is_empty()) {
|
||||
generator.emit<Bytecode::Op::NewArray>(strings_array);
|
||||
generator.emit<Bytecode::Op::NewArray>(strings_array, ReadonlySpan<ScopedOperand> {});
|
||||
} else {
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(string_regs.size(), strings_array, string_regs);
|
||||
}
|
||||
|
|
@ -2532,7 +2532,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TaggedTemplateLiteral::
|
|||
|
||||
auto raw_strings_array = generator.allocate_register();
|
||||
if (raw_string_regs.is_empty()) {
|
||||
generator.emit<Bytecode::Op::NewArray>(raw_strings_array);
|
||||
generator.emit<Bytecode::Op::NewArray>(raw_strings_array, ReadonlySpan<ScopedOperand> {});
|
||||
} else {
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(raw_string_regs.size(), raw_strings_array, raw_string_regs);
|
||||
}
|
||||
|
|
@ -2543,10 +2543,10 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TaggedTemplateLiteral::
|
|||
if (!argument_regs.is_empty())
|
||||
generator.emit_with_extra_operand_slots<Bytecode::Op::NewArray>(argument_regs.size(), arguments, argument_regs);
|
||||
else
|
||||
generator.emit<Bytecode::Op::NewArray>(arguments);
|
||||
generator.emit<Bytecode::Op::NewArray>(arguments, ReadonlySpan<ScopedOperand> {});
|
||||
|
||||
auto dst = choose_dst(generator, preferred_dst);
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(dst, tag, this_value, arguments);
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(dst, tag, this_value, arguments, OptionalNone {});
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
|
@ -2665,7 +2665,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TryStatement::generate_
|
|||
generator.begin_variable_scope();
|
||||
did_create_variable_scope_for_catch_clause = true;
|
||||
auto parameter_identifier = generator.intern_identifier(parameter->string());
|
||||
generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false, false, false);
|
||||
generator.emit<Bytecode::Op::InitializeLexicalBinding>(parameter_identifier, caught_value);
|
||||
}
|
||||
return {};
|
||||
|
|
@ -2683,7 +2683,7 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> TryStatement::generate_
|
|||
if (identifier.is_local())
|
||||
return;
|
||||
auto parameter_identifier = generator.intern_identifier(identifier.string());
|
||||
generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false, false, false);
|
||||
}));
|
||||
|
||||
TRY(binding_pattern->generate_bytecode(generator, Bytecode::Op::BindingInitializationMode::Initialize, caught_value));
|
||||
|
|
@ -2885,12 +2885,12 @@ Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ClassDeclaration::gener
|
|||
Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<Bytecode::IdentifierTableIndex> lhs_name, Optional<ScopedOperand> preferred_dst) const
|
||||
{
|
||||
// NOTE: Step 2 is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
|
||||
generator.emit<Bytecode::Op::CreateLexicalEnvironment>();
|
||||
generator.emit<Bytecode::Op::CreateLexicalEnvironment>(OptionalNone {}, 0);
|
||||
|
||||
if (has_name() || !lhs_name.has_value()) {
|
||||
// NOTE: Step 3.a is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
|
||||
auto interned_index = generator.intern_identifier(name());
|
||||
generator.emit<Bytecode::Op::CreateVariable>(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true, false, false);
|
||||
}
|
||||
|
||||
Optional<ScopedOperand> super_class;
|
||||
|
|
@ -3098,7 +3098,7 @@ static Bytecode::CodeGenerationErrorOr<ForInOfHeadEvaluationResult> for_in_of_he
|
|||
return;
|
||||
// i. Perform ! newEnv.CreateMutableBinding(name, false).
|
||||
auto interned_identifier = generator.intern_identifier(identifier.string());
|
||||
generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false, false, false);
|
||||
}));
|
||||
// d. Set the running execution context's LexicalEnvironment to newEnv.
|
||||
// NOTE: Done by CreateLexicalEnvironment.
|
||||
|
|
@ -3298,7 +3298,7 @@ static Bytecode::CodeGenerationErrorOr<Optional<ScopedOperand>> for_in_of_body_e
|
|||
// b. Else,
|
||||
else {
|
||||
// i. Perform ! environment.CreateMutableBinding(name, false).
|
||||
generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
|
||||
generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false, false, false);
|
||||
}
|
||||
}));
|
||||
// 3. Return unused.
|
||||
|
|
@ -3512,7 +3512,7 @@ static Bytecode::CodeGenerationErrorOr<void> generate_optional_chain(Bytecode::G
|
|||
TRY(reference.visit(
|
||||
[&](OptionalChain::Call const& call) -> Bytecode::CodeGenerationErrorOr<void> {
|
||||
auto arguments = TRY(arguments_to_array_for_call(generator, call.arguments)).value();
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(current_value, current_value, current_base, arguments);
|
||||
generator.emit<Bytecode::Op::CallWithArgumentArray>(current_value, current_value, current_base, arguments, OptionalNone {});
|
||||
generator.emit_mov(current_base, generator.add_constant(js_undefined()));
|
||||
return {};
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue