mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 06:38:10 +00:00
LibJS/JIT: Add fastpath for set variable
This commit is contained in:
parent
b3cbe0fdb9
commit
84eecbb10e
8 changed files with 148 additions and 12 deletions
|
@ -1080,7 +1080,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionDeclaration::generate_bytecode(Byt
|
||||||
Bytecode::Generator::SourceLocationScope scope(generator, *this);
|
Bytecode::Generator::SourceLocationScope scope(generator, *this);
|
||||||
auto index = generator.intern_identifier(name());
|
auto index = generator.intern_identifier(name());
|
||||||
generator.emit<Bytecode::Op::GetVariable>(index, generator.next_environment_variable_cache());
|
generator.emit<Bytecode::Op::GetVariable>(index, generator.next_environment_variable_cache());
|
||||||
generator.emit<Bytecode::Op::SetVariable>(index, Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var);
|
generator.emit<Bytecode::Op::SetVariable>(index, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode::Var);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1101,7 +1101,7 @@ Bytecode::CodeGenerationErrorOr<void> FunctionExpression::generate_bytecode_with
|
||||||
generator.emit_new_function(*this, lhs_name);
|
generator.emit_new_function(*this, lhs_name);
|
||||||
|
|
||||||
if (has_name) {
|
if (has_name) {
|
||||||
generator.emit<Bytecode::Op::SetVariable>(*name_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
|
generator.emit<Bytecode::Op::SetVariable>(*name_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
|
||||||
generator.end_variable_scope();
|
generator.end_variable_scope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2368,7 +2368,7 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode::
|
||||||
did_create_variable_scope_for_catch_clause = true;
|
did_create_variable_scope_for_catch_clause = true;
|
||||||
auto parameter_identifier = generator.intern_identifier(parameter);
|
auto parameter_identifier = generator.intern_identifier(parameter);
|
||||||
generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
|
generator.emit<Bytecode::Op::CreateVariable>(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
|
||||||
generator.emit<Bytecode::Op::SetVariable>(parameter_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize);
|
generator.emit<Bytecode::Op::SetVariable>(parameter_identifier, generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
@ -3196,7 +3196,7 @@ Bytecode::CodeGenerationErrorOr<void> ExportStatement::generate_bytecode(Bytecod
|
||||||
TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<ClassExpression const&>(*m_statement), generator.intern_identifier("default"sv)));
|
TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<ClassExpression const&>(*m_statement), generator.intern_identifier("default"sv)));
|
||||||
|
|
||||||
if (!static_cast<ClassExpression const&>(*m_statement).has_name())
|
if (!static_cast<ClassExpression const&>(*m_statement).has_name())
|
||||||
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), Bytecode::Op::SetVariable::InitializationMode::Initialize);
|
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -3204,7 +3204,7 @@ Bytecode::CodeGenerationErrorOr<void> ExportStatement::generate_bytecode(Bytecod
|
||||||
// ExportDeclaration : export default AssignmentExpression ;
|
// ExportDeclaration : export default AssignmentExpression ;
|
||||||
VERIFY(is<Expression>(*m_statement));
|
VERIFY(is<Expression>(*m_statement));
|
||||||
TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<Expression const&>(*m_statement), generator.intern_identifier("default"sv)));
|
TRY(generator.emit_named_evaluation_if_anonymous_function(static_cast<Expression const&>(*m_statement), generator.intern_identifier("default"sv)));
|
||||||
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), Bytecode::Op::SetVariable::InitializationMode::Initialize);
|
generator.emit<Bytecode::Op::SetVariable>(generator.intern_identifier(ExportStatement::local_name_for_default), generator.next_environment_variable_cache(), Bytecode::Op::SetVariable::InitializationMode::Initialize);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,10 +264,13 @@ ThrowCompletionOr<void> set_variable(
|
||||||
DeprecatedFlyString const& name,
|
DeprecatedFlyString const& name,
|
||||||
Value value,
|
Value value,
|
||||||
Op::EnvironmentMode mode,
|
Op::EnvironmentMode mode,
|
||||||
Op::SetVariable::InitializationMode initialization_mode)
|
Op::SetVariable::InitializationMode initialization_mode,
|
||||||
|
EnvironmentVariableCache& cache)
|
||||||
{
|
{
|
||||||
auto environment = mode == Op::EnvironmentMode::Lexical ? vm.running_execution_context().lexical_environment : vm.running_execution_context().variable_environment;
|
auto environment = mode == Op::EnvironmentMode::Lexical ? vm.running_execution_context().lexical_environment : vm.running_execution_context().variable_environment;
|
||||||
auto reference = TRY(vm.resolve_binding(name, environment));
|
auto reference = TRY(vm.resolve_binding(name, environment));
|
||||||
|
if (reference.environment_coordinate().has_value())
|
||||||
|
cache = reference.environment_coordinate();
|
||||||
switch (initialization_mode) {
|
switch (initialization_mode) {
|
||||||
case Op::SetVariable::InitializationMode::Initialize:
|
case Op::SetVariable::InitializationMode::Initialize:
|
||||||
TRY(reference.initialize_referenced_binding(vm, value));
|
TRY(reference.initialize_referenced_binding(vm, value));
|
||||||
|
|
|
@ -20,7 +20,7 @@ ThrowCompletionOr<void> put_by_property_key(VM&, Value base, Value this_value, V
|
||||||
ThrowCompletionOr<Value> perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector<Value> argument_values);
|
ThrowCompletionOr<Value> perform_call(Interpreter&, Value this_value, Op::CallType, Value callee, MarkedVector<Value> argument_values);
|
||||||
ThrowCompletionOr<void> throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional<StringTableIndex> const& expression_string);
|
ThrowCompletionOr<void> throw_if_needed_for_call(Interpreter&, Value callee, Op::CallType, Optional<StringTableIndex> const& expression_string);
|
||||||
ThrowCompletionOr<Value> typeof_variable(VM&, DeprecatedFlyString const&);
|
ThrowCompletionOr<Value> typeof_variable(VM&, DeprecatedFlyString const&);
|
||||||
ThrowCompletionOr<void> set_variable(VM&, DeprecatedFlyString const&, Value, Op::EnvironmentMode, Op::SetVariable::InitializationMode);
|
ThrowCompletionOr<void> set_variable(VM&, DeprecatedFlyString const&, Value, Op::EnvironmentMode, Op::SetVariable::InitializationMode, EnvironmentVariableCache&);
|
||||||
Value new_function(VM&, FunctionExpression const&, Optional<IdentifierTableIndex> const& lhs_name, Optional<Register> const& home_object);
|
Value new_function(VM&, FunctionExpression const&, Optional<IdentifierTableIndex> const& lhs_name, Optional<Register> const& home_object);
|
||||||
ThrowCompletionOr<void> put_by_value(VM&, Value base, Value property_key_value, Value value, Op::PropertyKind);
|
ThrowCompletionOr<void> put_by_value(VM&, Value base, Value property_key_value, Value value, Op::PropertyKind);
|
||||||
ThrowCompletionOr<Value> get_variable(Bytecode::Interpreter&, DeprecatedFlyString const& name, EnvironmentVariableCache&);
|
ThrowCompletionOr<Value> get_variable(Bytecode::Interpreter&, DeprecatedFlyString const& name, EnvironmentVariableCache&);
|
||||||
|
|
|
@ -414,7 +414,7 @@ void Generator::emit_set_variable(JS::Identifier const& identifier, Bytecode::Op
|
||||||
if (identifier.is_local()) {
|
if (identifier.is_local()) {
|
||||||
emit<Bytecode::Op::SetLocal>(identifier.local_variable_index());
|
emit<Bytecode::Op::SetLocal>(identifier.local_variable_index());
|
||||||
} else {
|
} else {
|
||||||
emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string()), initialization_mode, mode);
|
emit<Bytecode::Op::SetVariable>(intern_identifier(identifier.string()), next_environment_variable_cache(), initialization_mode, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -780,7 +780,12 @@ ThrowCompletionOr<void> SetVariable::execute_impl(Bytecode::Interpreter& interpr
|
||||||
{
|
{
|
||||||
auto& vm = interpreter.vm();
|
auto& vm = interpreter.vm();
|
||||||
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
|
auto const& name = interpreter.current_executable().get_identifier(m_identifier);
|
||||||
TRY(set_variable(vm, name, interpreter.accumulator(), m_mode, m_initialization_mode));
|
TRY(set_variable(vm,
|
||||||
|
name,
|
||||||
|
interpreter.accumulator(),
|
||||||
|
m_mode,
|
||||||
|
m_initialization_mode,
|
||||||
|
interpreter.current_executable().environment_variable_caches[m_cache_index]));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -472,11 +472,12 @@ public:
|
||||||
Initialize,
|
Initialize,
|
||||||
Set,
|
Set,
|
||||||
};
|
};
|
||||||
explicit SetVariable(IdentifierTableIndex identifier, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical)
|
explicit SetVariable(IdentifierTableIndex identifier, u32 cache_index, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical)
|
||||||
: Instruction(Type::SetVariable, sizeof(*this))
|
: Instruction(Type::SetVariable, sizeof(*this))
|
||||||
, m_identifier(identifier)
|
, m_identifier(identifier)
|
||||||
, m_mode(mode)
|
, m_mode(mode)
|
||||||
, m_initialization_mode(initialization_mode)
|
, m_initialization_mode(initialization_mode)
|
||||||
|
, m_cache_index(cache_index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,11 +487,13 @@ public:
|
||||||
IdentifierTableIndex identifier() const { return m_identifier; }
|
IdentifierTableIndex identifier() const { return m_identifier; }
|
||||||
EnvironmentMode mode() const { return m_mode; }
|
EnvironmentMode mode() const { return m_mode; }
|
||||||
InitializationMode initialization_mode() const { return m_initialization_mode; }
|
InitializationMode initialization_mode() const { return m_initialization_mode; }
|
||||||
|
u32 cache_index() const { return m_cache_index; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IdentifierTableIndex m_identifier;
|
IdentifierTableIndex m_identifier;
|
||||||
EnvironmentMode m_mode;
|
EnvironmentMode m_mode;
|
||||||
InitializationMode m_initialization_mode { InitializationMode::Set };
|
InitializationMode m_initialization_mode { InitializationMode::Set };
|
||||||
|
u32 m_cache_index { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetLocal final : public Instruction {
|
class SetLocal final : public Instruction {
|
||||||
|
|
|
@ -2736,18 +2736,140 @@ static Value cxx_set_variable(
|
||||||
DeprecatedFlyString const& identifier,
|
DeprecatedFlyString const& identifier,
|
||||||
Value value,
|
Value value,
|
||||||
Bytecode::Op::EnvironmentMode environment_mode,
|
Bytecode::Op::EnvironmentMode environment_mode,
|
||||||
Bytecode::Op::SetVariable::InitializationMode initialization_mode)
|
Bytecode::Op::SetVariable::InitializationMode initialization_mode,
|
||||||
|
Bytecode::EnvironmentVariableCache& cache)
|
||||||
{
|
{
|
||||||
TRY_OR_SET_EXCEPTION(Bytecode::set_variable(vm, identifier, value, environment_mode, initialization_mode));
|
TRY_OR_SET_EXCEPTION(Bytecode::set_variable(vm, identifier, value, environment_mode, initialization_mode, cache));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op)
|
void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op)
|
||||||
{
|
{
|
||||||
|
Assembler::Label slow_case;
|
||||||
|
|
||||||
|
// Load the identifier in ARG1 for both cases
|
||||||
m_assembler.mov(
|
m_assembler.mov(
|
||||||
Assembler::Operand::Register(ARG1),
|
Assembler::Operand::Register(ARG1),
|
||||||
Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.get_identifier(op.identifier().value()))));
|
Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.get_identifier(op.identifier().value()))));
|
||||||
|
|
||||||
|
// Load the value in ARG2 for both cases
|
||||||
load_accumulator(ARG2);
|
load_accumulator(ARG2);
|
||||||
|
|
||||||
|
// if (!cache.has_value()) goto slow_case;
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler::Operand::Register(ARG5),
|
||||||
|
Assembler::Operand::Imm(bit_cast<u64>(&m_bytecode_executable.environment_variable_caches[op.cache_index()])));
|
||||||
|
|
||||||
|
m_assembler.mov8(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(ARG5, Bytecode::EnvironmentVariableCache::has_value_offset()));
|
||||||
|
|
||||||
|
m_assembler.jump_if(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Condition::EqualTo,
|
||||||
|
Assembler::Operand::Imm(0),
|
||||||
|
slow_case);
|
||||||
|
|
||||||
|
if (op.mode() == Bytecode::Op::EnvironmentMode::Lexical) {
|
||||||
|
// auto environment = vm.running_execution_context().lexical_environment;
|
||||||
|
// GPR1 = current lexical environment
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(RUNNING_EXECUTION_CONTEXT_BASE, ExecutionContext::lexical_environment_offset()));
|
||||||
|
} else {
|
||||||
|
// auto environment = vm.running_execution_context().variable_environment;
|
||||||
|
// GPR1 = current variable environment
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(RUNNING_EXECUTION_CONTEXT_BASE, ExecutionContext::variable_environment_offset()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// for (size_t i = 0; i < cache->hops; ++i)
|
||||||
|
// environment = environment->outer_environment();
|
||||||
|
|
||||||
|
// GPR0 = hops
|
||||||
|
m_assembler.mov32(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(ARG5, Bytecode::EnvironmentVariableCache::value_offset() + EnvironmentCoordinate::hops_offset()));
|
||||||
|
|
||||||
|
{
|
||||||
|
// while (GPR0--)
|
||||||
|
// GPR1 = GPR1->outer_environment()
|
||||||
|
Assembler::Label loop_start;
|
||||||
|
Assembler::Label loop_end;
|
||||||
|
loop_start.link(m_assembler);
|
||||||
|
m_assembler.jump_if(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Condition::EqualTo,
|
||||||
|
Assembler::Operand::Imm(0),
|
||||||
|
loop_end);
|
||||||
|
m_assembler.sub(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Imm(1));
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(GPR1, Environment::outer_environment_offset()));
|
||||||
|
m_assembler.jump(loop_start);
|
||||||
|
loop_end.link(m_assembler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPR1 now points to the environment holding our binding.
|
||||||
|
|
||||||
|
// if (environment->is_permanently_screwed_by_eval()) goto slow_case;
|
||||||
|
m_assembler.mov8(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(GPR1, Environment::is_permanently_screwed_by_eval_offset()));
|
||||||
|
m_assembler.jump_if(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Condition::NotEqualTo,
|
||||||
|
Assembler::Operand::Imm(0),
|
||||||
|
slow_case);
|
||||||
|
|
||||||
|
// GPR1 = environment->m_bindings.outline_buffer()
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::bindings_offset() + Vector<DeclarativeEnvironment::Binding>::outline_buffer_offset()));
|
||||||
|
|
||||||
|
// GPR0 = index
|
||||||
|
m_assembler.mov32(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(ARG5, Bytecode::EnvironmentVariableCache::value_offset() + EnvironmentCoordinate::index_offset()));
|
||||||
|
|
||||||
|
// GPR0 *= sizeof(DeclarativeEnvironment::Binding)
|
||||||
|
m_assembler.mul32(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Imm(sizeof(DeclarativeEnvironment::Binding)),
|
||||||
|
slow_case);
|
||||||
|
|
||||||
|
// GPR1 = &binding
|
||||||
|
m_assembler.add(
|
||||||
|
Assembler::Operand::Register(GPR1),
|
||||||
|
Assembler::Operand::Register(GPR0));
|
||||||
|
|
||||||
|
// if (!binding.initialized) goto slow_case;
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler ::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::Binding::initialized_offset()));
|
||||||
|
m_assembler.bitwise_and(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Operand::Imm(0xff));
|
||||||
|
m_assembler.jump_if(
|
||||||
|
Assembler::Operand::Register(GPR0),
|
||||||
|
Assembler::Condition::EqualTo,
|
||||||
|
Assembler::Operand::Imm(0),
|
||||||
|
slow_case);
|
||||||
|
|
||||||
|
// binding.value = accumulator;
|
||||||
|
m_assembler.mov(
|
||||||
|
Assembler::Operand::Mem64BaseAndOffset(GPR1, DeclarativeEnvironment::Binding::value_offset()),
|
||||||
|
Assembler::Operand::Register(ARG2));
|
||||||
|
|
||||||
|
Assembler::Label end;
|
||||||
|
m_assembler.jump(end);
|
||||||
|
|
||||||
|
// Slow case: Uncached access. Call C++ helper.
|
||||||
|
slow_case.link(m_assembler);
|
||||||
|
|
||||||
m_assembler.mov(
|
m_assembler.mov(
|
||||||
Assembler::Operand::Register(ARG3),
|
Assembler::Operand::Register(ARG3),
|
||||||
Assembler::Operand::Imm(to_underlying(op.mode())));
|
Assembler::Operand::Imm(to_underlying(op.mode())));
|
||||||
|
@ -2756,6 +2878,8 @@ void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op)
|
||||||
Assembler::Operand::Imm(to_underlying(op.initialization_mode())));
|
Assembler::Operand::Imm(to_underlying(op.initialization_mode())));
|
||||||
native_call((void*)cxx_set_variable);
|
native_call((void*)cxx_set_variable);
|
||||||
check_exception();
|
check_exception();
|
||||||
|
|
||||||
|
end.link(m_assembler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile_continue_pending_unwind(Bytecode::Op::ContinuePendingUnwind const& op)
|
void Compiler::compile_continue_pending_unwind(Bytecode::Op::ContinuePendingUnwind const& op)
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct ExecutionContext {
|
||||||
|
|
||||||
static FlatPtr realm_offset() { return OFFSET_OF(ExecutionContext, realm); }
|
static FlatPtr realm_offset() { return OFFSET_OF(ExecutionContext, realm); }
|
||||||
static FlatPtr lexical_environment_offset() { return OFFSET_OF(ExecutionContext, lexical_environment); }
|
static FlatPtr lexical_environment_offset() { return OFFSET_OF(ExecutionContext, lexical_environment); }
|
||||||
|
static FlatPtr variable_environment_offset() { return OFFSET_OF(ExecutionContext, variable_environment); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ExecutionContext(MarkedVector<Value> existing_arguments, MarkedVector<Value> existing_local_variables);
|
explicit ExecutionContext(MarkedVector<Value> existing_arguments, MarkedVector<Value> existing_local_variables);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue