LibJS/Bytecode: Keep completion value of switch case if falling through

We do this by moving the `LoadImmediate undefined` instruction to a
separate basic block which jumps to the case's block unconditionally.
We enter a case initially using this wrapper, but when falling through,
we directly jump to the next case's block.
This commit is contained in:
Daniel Bertalan 2023-06-28 11:45:19 +02:00 committed by Andreas Kling
parent 02b74e5a70
commit 50d6b1ba19
Notes: sideshowbarker 2024-07-17 01:27:18 +09:00

View file

@ -2196,7 +2196,7 @@ Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_labelled_evaluat
TRY(m_discriminant->generate_bytecode(generator));
generator.emit<Bytecode::Op::Store>(discriminant_reg);
Vector<Bytecode::BasicBlock&> case_blocks;
Bytecode::BasicBlock* default_block { nullptr };
Bytecode::BasicBlock* entry_block_for_default { nullptr };
Bytecode::BasicBlock* next_test_block = &generator.make_block();
auto has_lexical_declarations = this->has_lexical_declarations();
@ -2207,22 +2207,30 @@ Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_labelled_evaluat
for (auto& switch_case : m_cases) {
auto& case_block = generator.make_block();
auto& case_entry_block = generator.make_block();
if (switch_case->test()) {
generator.switch_to_basic_block(*next_test_block);
TRY(switch_case->test()->generate_bytecode(generator));
generator.emit<Bytecode::Op::StrictlyEquals>(discriminant_reg);
next_test_block = &generator.make_block();
generator.emit<Bytecode::Op::JumpConditional>().set_targets(Bytecode::Label { case_block }, Bytecode::Label { *next_test_block });
generator.emit<Bytecode::Op::JumpConditional>().set_targets(Bytecode::Label { case_entry_block }, Bytecode::Label { *next_test_block });
} else {
default_block = &case_block;
entry_block_for_default = &case_entry_block;
}
// Initialize the completion value of the switch statement to empty. We can't do this in the case's basic block directly,
// as we must not clobber the possible non-empty completion value of the previous case when falling through.
generator.switch_to_basic_block(case_entry_block);
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { case_block }, {});
case_blocks.append(case_block);
}
generator.switch_to_basic_block(*next_test_block);
auto& end_block = generator.make_block();
if (default_block != nullptr) {
generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { *default_block }, {});
if (entry_block_for_default != nullptr) {
generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { *entry_block_for_default }, {});
} else {
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { end_block }, {});
@ -2232,7 +2240,6 @@ Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_labelled_evaluat
for (auto& switch_case : m_cases) {
generator.switch_to_basic_block(*current_block);
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
for (auto& statement : switch_case->children()) {
TRY(statement->generate_bytecode(generator));
if (generator.is_current_block_terminated())