From 50d6b1ba19121319a96dd9eb92c0de0cbca1bf10 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Wed, 28 Jun 2023 11:45:19 +0200 Subject: [PATCH] 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. --- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 3c0db0bba8e..6fb424ed589 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -2196,7 +2196,7 @@ Bytecode::CodeGenerationErrorOr SwitchStatement::generate_labelled_evaluat TRY(m_discriminant->generate_bytecode(generator)); generator.emit(discriminant_reg); Vector 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 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(discriminant_reg); next_test_block = &generator.make_block(); - generator.emit().set_targets(Bytecode::Label { case_block }, Bytecode::Label { *next_test_block }); + generator.emit().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(js_undefined()); + generator.emit().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().set_targets(Bytecode::Label { *default_block }, {}); + if (entry_block_for_default != nullptr) { + generator.emit().set_targets(Bytecode::Label { *entry_block_for_default }, {}); } else { generator.emit(js_undefined()); generator.emit().set_targets(Bytecode::Label { end_block }, {}); @@ -2232,7 +2240,6 @@ Bytecode::CodeGenerationErrorOr SwitchStatement::generate_labelled_evaluat for (auto& switch_case : m_cases) { generator.switch_to_basic_block(*current_block); - generator.emit(js_undefined()); for (auto& statement : switch_case->children()) { TRY(statement->generate_bytecode(generator)); if (generator.is_current_block_terminated())