From f5b385ca6c8088552e19f98b81f952da3227cf45 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 15 May 2017 09:00:57 +0200 Subject: [PATCH] Tracking variables to reduce the memory consumption of scope chain. (#1819) Currently we keep all local variables in the scope chain as long as a nested function is alive. These variables consume memory although several of them are never used. This patch focuses on removing those unused ones whose can be detected at parsing time. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/parser/js/common.h | 6 +- jerry-core/parser/js/js-lexer.c | 13 +- jerry-core/parser/js/js-parser-expr.c | 15 +-- jerry-core/parser/js/js-parser-internal.h | 2 +- jerry-core/parser/js/js-parser-statm.c | 1 - jerry-core/parser/js/js-parser.c | 137 ++++++++++++++++++++-- jerry-core/vm/vm.c | 38 +++--- 7 files changed, 168 insertions(+), 44 deletions(-) diff --git a/jerry-core/parser/js/common.h b/jerry-core/parser/js/common.h index c9db1de08..6829d0a3f 100644 --- a/jerry-core/parser/js/common.h +++ b/jerry-core/parser/js/common.h @@ -67,10 +67,12 @@ typedef enum #define LEXER_FLAG_FUNCTION_NAME 0x08 /* This local identifier is a function argument. */ #define LEXER_FLAG_FUNCTION_ARGUMENT 0x10 +/* This local identifier is not used in the current context. */ +#define LEXER_FLAG_UNUSED_IDENT 0x20 /* No space is allocated for this character literal. */ -#define LEXER_FLAG_SOURCE_PTR 0x20 +#define LEXER_FLAG_SOURCE_PTR 0x40 /* Initialize this variable after the byte code is freed. */ -#define LEXER_FLAG_LATE_INIT 0x40 +#define LEXER_FLAG_LATE_INIT 0x80 /** * Literal data. diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index 565dffe13..71db7e624 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -1183,6 +1183,7 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ { context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; + literal_p->status_flags = (uint8_t) (literal_p->status_flags & ~LEXER_FLAG_UNUSED_IDENT); return; } @@ -1611,13 +1612,16 @@ lexer_construct_number_object (parser_context_t *context_p, /**< context */ /** * Construct a function literal object. + * + * @return function object literal index */ -void +uint16_t lexer_construct_function_object (parser_context_t *context_p, /**< context */ uint32_t extra_status_flags) /**< extra status flags */ { ecma_compiled_code_t *compiled_code_p; lexer_literal_t *literal_p; + uint16_t result_index; if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { @@ -1629,20 +1633,19 @@ lexer_construct_function_object (parser_context_t *context_p, /**< context */ extra_status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; } - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; - literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->type = LEXER_UNUSED_LITERAL; literal_p->status_flags = 0; + result_index = context_p->literal_count; context_p->literal_count++; compiled_code_p = parser_parse_function (context_p, extra_status_flags); literal_p->u.bytecode_p = compiled_code_p; - literal_p->type = LEXER_FUNCTION_LITERAL; - context_p->status_flags |= PARSER_NO_REG_STORE; + + return result_index; } /* lexer_construct_function_object */ /** diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index fe93bbdb8..cac23439f 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -377,7 +377,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ { uint32_t status_flags; cbc_ext_opcode_t opcode; - uint16_t literal_index; + uint16_t literal_index, function_literal_index; parser_object_literal_item_types_t item_type; if (context_p->token.type == LEXER_PROPERTY_GETTER) @@ -404,7 +404,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ parser_append_object_literal_item (context_p, literal_index, item_type); parser_flush_cbc (context_p); - lexer_construct_function_object (context_p, status_flags); + function_literal_index = lexer_construct_function_object (context_p, status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, @@ -412,7 +412,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); - context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1); + context_p->last_cbc.value = function_literal_index; lexer_next_token (context_p); } @@ -576,6 +576,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ int literals = 0; uint16_t literal1 = 0; uint16_t literal2 = 0; + uint16_t function_literal_index; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { @@ -602,7 +603,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; } - lexer_construct_function_object (context_p, status_flags); + function_literal_index = lexer_construct_function_object (context_p, status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); @@ -610,20 +611,20 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.literal_index = literal1; - context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1); + context_p->last_cbc.value = function_literal_index; } else if (literals == 2) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = literal2; - context_p->last_cbc.third_literal_index = (uint16_t) (context_p->literal_count - 1); + context_p->last_cbc.third_literal_index = function_literal_index; } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, - (uint16_t) (context_p->literal_count - 1)); + function_literal_index); } context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 937554c68..fef667c7d 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -399,7 +399,7 @@ void lexer_expect_object_literal_id (parser_context_t *context_p, bool must_be_i void lexer_construct_literal_object (parser_context_t *context_p, lexer_lit_location_t *literal_p, uint8_t literal_type); bool lexer_construct_number_object (parser_context_t *context_p, bool push_number_allowed, bool is_negative_number); -void lexer_construct_function_object (parser_context_t *context_p, uint32_t extra_status_flags); +uint16_t lexer_construct_function_object (parser_context_t *context_p, uint32_t extra_status_flags); void lexer_construct_regexp_object (parser_context_t *context_p, bool parse_only); bool lexer_compare_identifier_to_current (parser_context_t *context_p, const lexer_lit_location_t *right); diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 840157843..ec0df8e35 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -393,7 +393,6 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ } name_p = context_p->lit_object.literal_p; - context_p->status_flags |= PARSER_NO_REG_STORE; status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 0bc933558..09c7a7261 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -32,6 +32,94 @@ * @{ */ +/** + * Copy identifiers if needed. + */ +static void +parser_copy_identifiers (parser_context_t *context_p) /**< context */ +{ + parser_saved_context_t *parent_p = context_p->last_context_p; + + if (parent_p == NULL || parent_p->prev_context_p == NULL) + { + /* Return if this function is not a nested function. */ + return; + } + + if (context_p->status_flags & PARSER_NO_REG_STORE) + { + /* This flag must affect all parent functions. */ + parent_p->status_flags |= PARSER_NO_REG_STORE; + return; + } + + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + + parser_list_t parent_literal_pool; + + /* Accessing the parent literal pool requires all data. */ + parent_literal_pool.data = parent_p->literal_pool_data; + parent_literal_pool.page_size = context_p->literal_pool.page_size; + parent_literal_pool.item_size = context_p->literal_pool.item_size; + parent_literal_pool.item_count = context_p->literal_pool.item_count; + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + if (literal_p->type != LEXER_IDENT_LITERAL || (literal_p->status_flags & LEXER_FLAG_VAR)) + { + continue; + } + + parser_list_iterator_t parent_literal_iterator; + parser_list_iterator_init (&parent_literal_pool, &parent_literal_iterator); + + lexer_literal_t *parent_literal_p; + const uint8_t *char_p = literal_p->u.char_p; + size_t length = literal_p->prop.length; + + while ((parent_literal_p = (lexer_literal_t *) parser_list_iterator_next (&parent_literal_iterator)) != NULL) + { + if (parent_literal_p->type == LEXER_IDENT_LITERAL + && parent_literal_p->prop.length == length + && memcmp (parent_literal_p->u.char_p, char_p, length) == 0) + { + /* This literal is known by the parent. */ + parent_literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + break; + } + } + + if (parent_literal_p != NULL) + { + continue; + } + + if (parent_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + parent_literal_p = (lexer_literal_t *) parser_list_append (context_p, &parent_literal_pool); + + /* The literal data is updated at every iteration to handle out-of memory. */ + parent_p->literal_pool_data = parent_literal_pool.data; + + parent_literal_p->prop.length = (uint16_t) length; + parent_literal_p->type = LEXER_IDENT_LITERAL; + parent_literal_p->status_flags = (uint8_t) (literal_p->status_flags & LEXER_FLAG_SOURCE_PTR); + parent_literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE | LEXER_FLAG_UNUSED_IDENT; + parent_literal_p->u.char_p = char_p; + + /* The buffer ownership is passed to the parent by + * setting this flag which prevents freeing the buffer. */ + literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; + + parent_p->literal_count++; + } +} /* parser_copy_identifiers */ + /** * Compute real literal indicies. * @@ -74,6 +162,12 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { + if (literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT) + { + context_p->literal_count--; + continue; + } + #ifndef PARSER_DUMP_BYTE_CODE if (literal_p->type == LEXER_IDENT_LITERAL || literal_p->type == LEXER_STRING_LITERAL) @@ -125,7 +219,7 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (literal_p == PARSER_GET_LITERAL (0)); - status_flags |= PARSER_NAMED_FUNCTION_EXP | PARSER_NO_REG_STORE | PARSER_LEXICAL_ENV_NEEDED; + status_flags |= PARSER_NAMED_FUNCTION_EXP; context_p->status_flags = status_flags; literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; @@ -174,6 +268,12 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; uninitialized_var_count++; } + + if (literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) + { + status_flags |= PARSER_LEXICAL_ENV_NEEDED; + context_p->status_flags = status_flags; + } } else { @@ -249,6 +349,11 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ { uint16_t init_index; + if (literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT) + { + continue; + } + if (literal_p->type != LEXER_IDENT_LITERAL) { if (literal_p->type == LEXER_STRING_LITERAL @@ -349,7 +454,8 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ } /* A CBC_INITIALIZE_VAR instruction or part of a CBC_INITIALIZE_VARS instruction. */ - if (!(status_flags & PARSER_HAS_INITIALIZED_VARS)) + if (!(status_flags & PARSER_HAS_INITIALIZED_VARS) + || !(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE)) { length += 2; if (literal_p->prop.index > literal_one_byte_limit) @@ -438,7 +544,6 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */ dst_p = parser_encode_literal (dst_p, (uint16_t) (uninitialized_var_end - 1), literal_one_byte_limit); - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; } if (context_p->status_flags & PARSER_HAS_INITIALIZED_VARS) @@ -517,9 +622,12 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */ || literal_p->type == LEXER_STRING_LITERAL) { #ifdef PARSER_DUMP_BYTE_CODE - jmem_cpointer_t lit_cp = ecma_find_or_create_literal_string (literal_p->u.char_p, - literal_p->prop.length); - literal_pool_p[literal_p->prop.index] = lit_cp; + if (!(literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT)) + { + jmem_cpointer_t lit_cp = ecma_find_or_create_literal_string (literal_p->u.char_p, + literal_p->prop.length); + literal_pool_p[literal_p->prop.index] = lit_cp; + } if (!context_p->is_show_opcodes && !(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) @@ -527,7 +635,10 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */ jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length); } #else /* !PARSER_DUMP_BYTE_CODE */ - literal_pool_p[literal_p->prop.index] = literal_p->u.value; + if (!(literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT)) + { + literal_pool_p[literal_p->prop.index] = literal_p->u.value; + } #endif /* PARSER_DUMP_BYTE_CODE */ } else if ((literal_p->type == LEXER_FUNCTION_LITERAL) @@ -555,8 +666,6 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */ JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME) { init_index = const_literal_end; @@ -869,7 +978,6 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * if (literal_p == NULL) { - if (literal_index == const_literal_end) { JERRY_DEBUG_MSG (" idx:%d(self)->function", literal_index); @@ -882,7 +990,8 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * } if (literal_p->prop.index == literal_index - && literal_p->type != LEXER_UNUSED_LITERAL) + && literal_p->type != LEXER_UNUSED_LITERAL + && !(literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT)) { JERRY_DEBUG_MSG (" idx:%d", literal_index); @@ -1293,8 +1402,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ } #endif /* JERRY_DEBUGGER */ - last_page_p = context_p->byte_code.last_p; - last_position = context_p->byte_code.last_position; + parser_copy_identifiers (context_p); initializers_length = parser_compute_indicies (context_p, &ident_end, @@ -1312,6 +1420,9 @@ parser_post_processing (parser_context_t *context_p) /**< context */ literal_one_byte_limit = CBC_LOWER_SEVEN_BIT_MASK; } + last_page_p = context_p->byte_code.last_p; + last_position = context_p->byte_code.last_position; + if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) { last_page_p = NULL; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 6f01ccb74..d66b7d518 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -630,8 +630,6 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { uint32_t value_index; ecma_value_t lit_value; - ecma_string_t *name_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, - literal_start_p[literal_index]); READ_LITERAL_INDEX (value_index); @@ -645,26 +643,36 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ literal_start_p[value_index]); } - if (self_reference == literal_start_p[value_index]) + if (literal_index < register_end) { - ecma_op_create_immutable_binding (frame_ctx_p->lex_env_p, name_p, lit_value); + frame_ctx_p->registers_p[literal_index] = lit_value; } else { - vm_var_decl (frame_ctx_p, name_p); + ecma_string_t *name_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, + literal_start_p[literal_index]); - ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, name_p); + if (self_reference == literal_start_p[value_index]) + { + ecma_op_create_immutable_binding (frame_ctx_p->lex_env_p, name_p, lit_value); + } + else + { + vm_var_decl (frame_ctx_p, name_p); - ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (ref_base_lex_env_p, - name_p, - is_strict, - lit_value); - ecma_free_value (put_value_result); - } + ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, name_p); - if (value_index >= register_end) - { - ecma_free_value (lit_value); + ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (ref_base_lex_env_p, + name_p, + is_strict, + lit_value); + ecma_free_value (put_value_result); + } + + if (value_index >= register_end) + { + ecma_free_value (lit_value); + } } literal_index++;