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
This commit is contained in:
Zoltan Herczeg 2017-05-15 09:00:57 +02:00 committed by GitHub
parent 8143a0cc10
commit f5b385ca6c
7 changed files with 168 additions and 44 deletions

View File

@ -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.

View File

@ -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 */
/**

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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++;