From c0fadfb9b73b6cc3e415c43e0f9ab8231aa74433 Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Sat, 11 Jun 2022 23:09:37 +0100 Subject: LibJS/Bytecode: Implement break/continue labels This is done by keeping track of all the labels that apply to a given break/continue scope alongside their bytecode target. When a break/continue with a label is generated, we scan from the most inner scope to the most outer scope looking for the label, performing any necessary unwinds on the way. Once the label is found, it is then jumped to. --- Userland/Libraries/LibJS/AST.h | 9 ++ Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 155 ++++++++++++++++++++--- Userland/Libraries/LibJS/Bytecode/Generator.cpp | 68 +++++++++- Userland/Libraries/LibJS/Bytecode/Generator.h | 16 ++- 4 files changed, 218 insertions(+), 30 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 743cd5bacd..62f3ef1686 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -113,6 +113,8 @@ public: virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual void dump(int indent) const override; + virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const; FlyString const& label() const { return m_label; } FlyString& label() { return m_label; } @@ -142,6 +144,7 @@ public: using Statement::Statement; virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector const&) const = 0; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const; private: virtual bool is_iteration_statement() const final { return true; } @@ -803,6 +806,7 @@ public: virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector const&) const override; virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const override; private: NonnullRefPtr m_test; @@ -825,6 +829,7 @@ public: virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector const&) const override; virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const override; private: NonnullRefPtr m_test; @@ -872,6 +877,7 @@ public: virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector const&) const override; virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const override; private: RefPtr m_init; @@ -896,6 +902,7 @@ public: virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const override; virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector const&) const override; virtual void dump(int indent) const override; @@ -921,6 +928,7 @@ public: virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const override; virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector const&) const override; virtual void dump(int indent) const override; @@ -2002,6 +2010,7 @@ public: virtual void dump(int indent) const override; virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const; Completion execute_impl(Interpreter&, GlobalObject&) const; void add_case(NonnullRefPtr switch_case) { m_cases.append(move(switch_case)); } diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index f35c25bfa8..8e4e322b60 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -618,7 +618,80 @@ Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(By return {}; } +// 14.13.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-labelled-statements-runtime-semantics-evaluation +// LabelledStatement : LabelIdentifier : LabelledItem +Bytecode::CodeGenerationErrorOr LabelledStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + // Return ? LabelledEvaluation of this LabelledStatement with argument « ». + return generate_labelled_evaluation(generator, {}); +} + +// 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation +// LabelledStatement : LabelIdentifier : LabelledItem +Bytecode::CodeGenerationErrorOr LabelledStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const +{ + // Convert the m_labelled_item NNRP to a reference early so we don't have to do it every single time we want to use it. + auto const& labelled_item = *m_labelled_item; + + // 1. Let label be the StringValue of LabelIdentifier. + // NOTE: Not necessary, this is m_label. + + // 2. Let newLabelSet be the list-concatenation of labelSet and « label ». + // FIXME: Avoid copy here. + auto new_label_set = label_set; + new_label_set.append(m_label); + + // 3. Let stmtResult be LabelledEvaluation of LabelledItem with argument newLabelSet. + // NOTE: stmtResult will be in the accumulator after running the generated bytecode. + if (is(labelled_item)) { + auto const& iteration_statement = static_cast(labelled_item); + TRY(iteration_statement.generate_labelled_evaluation(generator, new_label_set)); + } else if (is(labelled_item)) { + auto const& switch_statement = static_cast(labelled_item); + TRY(switch_statement.generate_labelled_evaluation(generator, new_label_set)); + } else if (is(labelled_item)) { + auto const& labelled_statement = static_cast(labelled_item); + TRY(labelled_statement.generate_labelled_evaluation(generator, new_label_set)); + } else { + auto& labelled_break_block = generator.make_block(); + + // NOTE: We do not need a continuable scope as `continue;` is not allowed outside of iteration statements, throwing a SyntaxError in the parser. + generator.begin_breakable_scope(Bytecode::Label { labelled_break_block }, new_label_set); + TRY(labelled_item.generate_bytecode(generator)); + generator.end_breakable_scope(); + + if (!generator.is_current_block_terminated()) { + generator.emit().set_targets( + Bytecode::Label { labelled_break_block }, + {}); + } + + generator.switch_to_basic_block(labelled_break_block); + } + + // 4. If stmtResult.[[Type]] is break and SameValue(stmtResult.[[Target]], label) is true, then + // a. Set stmtResult to NormalCompletion(stmtResult.[[Value]]). + // NOTE: These steps are performed by making labelled break jump straight to the appropriate break block, which preserves the statement result's value in the accumulator. + + // 5. Return Completion(stmtResult). + // NOTE: This is in the accumulator. + return {}; +} + +Bytecode::CodeGenerationErrorOr IterationStatement::generate_labelled_evaluation(Bytecode::Generator&, Vector const&) const +{ + return Bytecode::CodeGenerationError { + this, + "Missing generate_labelled_evaluation()"sv, + }; +} + Bytecode::CodeGenerationErrorOr WhileStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr WhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { // test // jump if_false (true) end (false) body @@ -646,8 +719,8 @@ Bytecode::CodeGenerationErrorOr WhileStatement::generate_bytecode(Bytecode Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); - generator.begin_continuable_scope(Bytecode::Label { test_block }); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_continuable_scope(Bytecode::Label { test_block }, label_set); + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); @@ -664,6 +737,11 @@ Bytecode::CodeGenerationErrorOr WhileStatement::generate_bytecode(Bytecode } Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { // jump always (true) body // test @@ -692,8 +770,8 @@ Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_bytecode(Byteco Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); - generator.begin_continuable_scope(Bytecode::Label { test_block }); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_continuable_scope(Bytecode::Label { test_block }, label_set); + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); @@ -710,6 +788,11 @@ Bytecode::CodeGenerationErrorOr DoWhileStatement::generate_bytecode(Byteco } Bytecode::CodeGenerationErrorOr ForStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr ForStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { // init // jump always (true) test @@ -732,6 +815,9 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_bytecode(Bytecode:: bool has_lexical_environment = false; + // The breakable scope needs to start here to unwind the potentially created lexical environment for the init bytecode. + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); + if (m_init) { if (m_init->is_variable_declaration()) { auto& variable_declaration = verify_cast(*m_init); @@ -783,10 +869,8 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_bytecode(Bytecode:: } generator.switch_to_basic_block(*body_block_ptr); - generator.begin_continuable_scope(Bytecode::Label { *update_block_ptr }); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_continuable_scope(Bytecode::Label { *update_block_ptr }, label_set); TRY(m_body->generate_bytecode(generator)); - generator.end_breakable_scope(); generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { @@ -810,6 +894,7 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_bytecode(Bytecode:: if (has_lexical_environment) generator.end_variable_scope(); + generator.end_breakable_scope(); return {}; } @@ -1345,9 +1430,17 @@ Bytecode::CodeGenerationErrorOr IfStatement::generate_bytecode(Bytecode::G Bytecode::CodeGenerationErrorOr ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const { - generator.perform_needed_unwinds(); + if (m_target_label.is_null()) { + generator.perform_needed_unwinds(); + generator.emit().set_targets( + generator.nearest_continuable_scope(), + {}); + return {}; + } + + auto target_to_jump_to = generator.perform_needed_unwinds_for_labelled_continue_and_return_target_block(m_target_label); generator.emit().set_targets( - generator.nearest_continuable_scope(), + target_to_jump_to, {}); return {}; } @@ -1524,9 +1617,17 @@ Bytecode::CodeGenerationErrorOr ThrowStatement::generate_bytecode(Bytecode Bytecode::CodeGenerationErrorOr BreakStatement::generate_bytecode(Bytecode::Generator& generator) const { - generator.perform_needed_unwinds(true); + if (m_target_label.is_null()) { + generator.perform_needed_unwinds(true); + generator.emit().set_targets( + generator.nearest_breakable_scope(), + {}); + return {}; + } + + auto target_to_jump_to = generator.perform_needed_unwinds_for_labelled_break_and_return_target_block(m_target_label); generator.emit().set_targets( - generator.nearest_breakable_scope(), + target_to_jump_to, {}); return {}; } @@ -1614,6 +1715,11 @@ Bytecode::CodeGenerationErrorOr TryStatement::generate_bytecode(Bytecode:: } Bytecode::CodeGenerationErrorOr SwitchStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { auto discriminant_reg = generator.allocate_register(); TRY(m_discriminant->generate_bytecode(generator)); @@ -1651,7 +1757,7 @@ Bytecode::CodeGenerationErrorOr SwitchStatement::generate_bytecode(Bytecod generator.emit().set_targets(Bytecode::Label { end_block }, {}); } auto current_block = case_blocks.begin(); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); for (auto& switch_case : m_cases) { generator.switch_to_basic_block(*current_block); @@ -1858,7 +1964,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he } // 14.7.5.7 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ), https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset -static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant, NonnullRefPtr> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update) +static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant, NonnullRefPtr> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Vector const& label_set, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update) { auto iterator_register = generator.allocate_register(); generator.emit(iterator_register); @@ -1888,6 +1994,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode: // 6. Repeat, generator.emit(Bytecode::Label { loop_update }); generator.switch_to_basic_block(loop_update); + generator.begin_continuable_scope(Bytecode::Label { loop_update }, label_set); // a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). generator.emit(iterator_register); @@ -2042,31 +2149,39 @@ static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode: return {}; } -// 14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-forinofloopevaluation Bytecode::CodeGenerationErrorOr ForInStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + return generate_labelled_evaluation(generator, {}); +} + +// 14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-forinofloopevaluation +Bytecode::CodeGenerationErrorOr ForInStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); - generator.begin_breakable_scope(Bytecode::Label { loop_end }); - generator.begin_continuable_scope(Bytecode::Label { loop_update }); + generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::Enumerate, m_lhs, m_rhs)); // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. - return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, loop_end, loop_update); + return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } Bytecode::CodeGenerationErrorOr ForOfStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr ForOfStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector const& label_set) const { auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); - generator.begin_breakable_scope(Bytecode::Label { loop_end }); - generator.begin_continuable_scope(Bytecode::Label { loop_update }); + generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::Iterate, m_lhs, m_rhs)); // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. - return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, loop_end, loop_update); + return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } // 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 74dff1df70..3dc1799104 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -69,7 +69,7 @@ Register Generator::allocate_register() Label Generator::nearest_continuable_scope() const { - return m_continuable_scopes.last(); + return m_continuable_scopes.last().bytecode_target; } void Generator::begin_variable_scope(BindingMode mode, SurroundingScopeKind kind) @@ -99,9 +99,9 @@ void Generator::end_variable_scope() } } -void Generator::begin_continuable_scope(Label continue_target) +void Generator::begin_continuable_scope(Label continue_target, Vector const& language_label_set) { - m_continuable_scopes.append(continue_target); + m_continuable_scopes.append({ continue_target, language_label_set }); start_boundary(BlockBoundaryType::Continue); } @@ -110,13 +110,15 @@ void Generator::end_continuable_scope() m_continuable_scopes.take_last(); end_boundary(BlockBoundaryType::Continue); } + Label Generator::nearest_breakable_scope() const { - return m_breakable_scopes.last(); + return m_breakable_scopes.last().bytecode_target; } -void Generator::begin_breakable_scope(Label breakable_target) + +void Generator::begin_breakable_scope(Label breakable_target, Vector const& language_label_set) { - m_breakable_scopes.append(breakable_target); + m_breakable_scopes.append({ breakable_target, language_label_set }); start_boundary(BlockBoundaryType::Break); } @@ -246,6 +248,60 @@ CodeGenerationErrorOr Generator::emit_delete_reference(JS::ASTNode const& return {}; } +Label Generator::perform_needed_unwinds_for_labelled_break_and_return_target_block(FlyString const& break_label) +{ + size_t current_boundary = m_boundaries.size(); + for (auto& breakable_scope : m_breakable_scopes.in_reverse()) { + for (; current_boundary > 0; --current_boundary) { + auto boundary = m_boundaries[current_boundary - 1]; + if (boundary == BlockBoundaryType::Unwind) { + emit(); + } else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) { + emit(Bytecode::Op::EnvironmentMode::Lexical); + } else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) { + emit(Bytecode::Op::EnvironmentMode::Var); + } else if (boundary == BlockBoundaryType::Break) { + // Make sure we don't process this boundary twice if the current breakable scope doesn't contain the target label. + --current_boundary; + break; + } + } + + if (breakable_scope.language_label_set.contains_slow(break_label)) + return breakable_scope.bytecode_target; + } + + // We must have a breakable scope available that contains the label, as this should be enforced by the parser. + VERIFY_NOT_REACHED(); +} + +Label Generator::perform_needed_unwinds_for_labelled_continue_and_return_target_block(FlyString const& continue_label) +{ + size_t current_boundary = m_boundaries.size(); + for (auto& continuable_scope : m_continuable_scopes.in_reverse()) { + for (; current_boundary > 0; --current_boundary) { + auto boundary = m_boundaries[current_boundary - 1]; + if (boundary == BlockBoundaryType::Unwind) { + emit(); + } else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) { + emit(Bytecode::Op::EnvironmentMode::Lexical); + } else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) { + emit(Bytecode::Op::EnvironmentMode::Var); + } else if (boundary == BlockBoundaryType::Continue) { + // Make sure we don't process this boundary twice if the current continuable scope doesn't contain the target label. + --current_boundary; + break; + } + } + + if (continuable_scope.language_label_set.contains_slow(continue_label)) + return continuable_scope.bytecode_target; + } + + // We must have a continuable scope available that contains the label, as this should be enforced by the parser. + VERIFY_NOT_REACHED(); +} + String CodeGenerationError::to_string() { return String::formatted("CodeGenerationError in {}: {}", failing_node ? failing_node->class_name() : "", reason_literal); diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index f63f9e7b0f..6af3c9bfdb 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -81,9 +81,9 @@ public: CodeGenerationErrorOr emit_store_to_reference(JS::ASTNode const&); CodeGenerationErrorOr emit_delete_reference(JS::ASTNode const&); - void begin_continuable_scope(Label continue_target); + void begin_continuable_scope(Label continue_target, Vector const& language_label_set); void end_continuable_scope(); - void begin_breakable_scope(Label breakable_target); + void begin_breakable_scope(Label breakable_target, Vector const& language_label_set); void end_breakable_scope(); [[nodiscard]] Label nearest_continuable_scope() const; @@ -186,6 +186,9 @@ public: } } + Label perform_needed_unwinds_for_labelled_break_and_return_target_block(FlyString const& break_label); + Label perform_needed_unwinds_for_labelled_continue_and_return_target_block(FlyString const& continue_label); + void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); } void end_boundary(BlockBoundaryType type) { @@ -200,6 +203,11 @@ private: void grow(size_t); void* next_slot(); + struct LabelableScope { + Label bytecode_target; + Vector language_label_set; + }; + BasicBlock* m_current_basic_block { nullptr }; NonnullOwnPtrVector m_root_basic_blocks; NonnullOwnPtr m_string_table; @@ -208,8 +216,8 @@ private: u32 m_next_register { 2 }; u32 m_next_block { 1 }; FunctionKind m_enclosing_function_kind { FunctionKind::Normal }; - Vector