General GC optimizations (#3221)

- Enable recursive GC marking with a limited recursion count (this option is configurable)
- No need to decrease the reference count of the gray objects anymore
- Bound function object marking is seperated into a helper function

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik
2019-10-24 14:49:47 +02:00
committed by Dániel Bátyai
parent 3b73562fa5
commit 48f34adea5
10 changed files with 176 additions and 92 deletions
+5
View File
@@ -41,6 +41,7 @@ set(JERRY_VM_EXEC_STOP OFF CACHE BOOL "Enable VM execution st
set(JERRY_GLOBAL_HEAP_SIZE "(512)" CACHE STRING "Size of memory heap, in kilobytes")
set(JERRY_GC_LIMIT "(0)" CACHE STRING "Heap usage limit to trigger garbage collection")
set(JERRY_STACK_LIMIT "(0)" CACHE STRING "Maximum stack usage size, in kilobytes")
set(JERRY_GC_MARK_LIMIT "(8)" CACHE STRING "Maximum depth of recursion during GC mark phase")
# Option overrides
if(USING_MSVC)
@@ -104,6 +105,7 @@ message(STATUS "JERRY_VM_EXEC_STOP " ${JERRY_VM_EXEC_STOP})
message(STATUS "JERRY_GLOBAL_HEAP_SIZE " ${JERRY_GLOBAL_HEAP_SIZE})
message(STATUS "JERRY_GC_LIMIT " ${JERRY_GC_LIMIT})
message(STATUS "JERRY_STACK_LIMIT " ${JERRY_STACK_LIMIT})
message(STATUS "JERRY_GC_MARK_LIMIT " ${JERRY_GC_MARK_LIMIT})
# Include directories
set(INCLUDE_CORE_PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -305,6 +307,9 @@ set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_GLOBAL_HEAP_SIZE=${JERRY_GLOBAL_HEAP_SI
# Maximum size of stack memory usage
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_STACK_LIMIT=${JERRY_STACK_LIMIT})
# Maximum depth of recursion during GC mark phase
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_GC_MARK_LIMIT=${JERRY_GC_MARK_LIMIT})
## This function is to read "config.h" for default values
function(read_set_defines FILE PREFIX OUTPUTVAR)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}" INPUT_FILE_CONTENTS)
+12
View File
@@ -201,6 +201,15 @@
# define JERRY_STACK_LIMIT (0)
#endif /* !defined (JERRY_STACK_LIMIT) */
/**
* Maximum depth of recursion during GC mark phase
*
* Default value: 8
*/
#ifndef JERRY_GC_MARK_LIMIT
# define JERRY_GC_MARK_LIMIT (8)
#endif /* !defined (JERRY_GC_MARK_LIMIT) */
/**
* Enable/Disable property lookup cache.
*
@@ -572,6 +581,9 @@
#if !defined (JERRY_STACK_LIMIT) || (JERRY_STACK_LIMIT < 0)
# error "Invalid value for 'JERRY_STACK_LIMIT' macro."
#endif
#if !defined (JERRY_GC_MARK_LIMIT) || (JERRY_GC_MARK_LIMIT < 0)
# error "Invalid value for 'JERRY_GC_MARK_LIMIT' macro."
#endif
#if !defined (JERRY_LCACHE) \
|| ((JERRY_LCACHE != 0) && (JERRY_LCACHE != 1))
# error "Invalid value for 'JERRY_LCACHE' macro."
+101 -76
View File
@@ -64,30 +64,42 @@ ecma_gc_is_object_visited (ecma_object_t *object_p) /**< object */
{
JERRY_ASSERT (object_p != NULL);
return (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE);
return (object_p->type_flags_refs < ECMA_OBJECT_NON_VISITED);
} /* ecma_gc_is_object_visited */
/**
* Set visited flag of the object.
* Note: This macro can be inlined for performance critical code paths
* Mark objects as visited starting from specified object as root
*/
#define ECMA_GC_SET_OBJECT_VISITED(object_p) \
do \
{ \
if ((object_p)->type_flags_refs < ECMA_OBJECT_REF_ONE) \
{ \
(object_p)->type_flags_refs |= ECMA_OBJECT_REF_ONE; \
} \
} while (0)
static void ecma_gc_mark (ecma_object_t *object_p);
/**
* Set visited flag of the object.
*/
static void JERRY_ATTR_NOINLINE
static void
ecma_gc_set_object_visited (ecma_object_t *object_p) /**< object */
{
/* Set reference counter to one if it is zero. */
ECMA_GC_SET_OBJECT_VISITED (object_p);
if (object_p->type_flags_refs >= ECMA_OBJECT_NON_VISITED)
{
#if (JERRY_GC_MARK_LIMIT != 0)
if (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) != 0)
{
JERRY_CONTEXT (ecma_gc_mark_recursion_limit)--;
/* Set the reference count of gray object to 0 */
object_p->type_flags_refs = (uint16_t) (object_p->type_flags_refs & (ECMA_OBJECT_REF_ONE - 1));
ecma_gc_mark (object_p);
JERRY_CONTEXT (ecma_gc_mark_recursion_limit)++;
}
else
{
/* Set the reference count of the non-marked gray object to 1 */
object_p->type_flags_refs = (uint16_t) (object_p->type_flags_refs & ((ECMA_OBJECT_REF_ONE << 1) - 1));
JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE);
}
#else /* (JERRY_GC_MARK_LIMIT == 0) */
/* Set the reference count of gray object to 0 */
object_p->type_flags_refs = (uint16_t) (object_p->type_flags_refs & (ECMA_OBJECT_REF_ONE - 1));
#endif /* (JERRY_GC_MARK_LIMIT != 0) */
}
} /* ecma_gc_set_object_visited */
/**
@@ -154,7 +166,7 @@ ecma_gc_mark_properties (ecma_property_pair_t *property_pair_p) /**< property pa
{
ecma_object_t *value_obj_p = ecma_get_object_from_value (value);
ECMA_GC_SET_OBJECT_VISITED (value_obj_p);
ecma_gc_set_object_visited (value_obj_p);
}
break;
}
@@ -193,8 +205,49 @@ ecma_gc_mark_properties (ecma_property_pair_t *property_pair_p) /**< property pa
}
} /* ecma_gc_mark_properties */
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
/**
* Mark objects referenced by bound function object.
*/
static void JERRY_ATTR_NOINLINE
ecma_gc_mark_bound_function_object (ecma_object_t *object_p) /**< bound function object */
{
JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION);
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) object_p;
ecma_object_t *target_func_obj_p;
target_func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ext_function_p->u.bound_function.target_function);
ecma_gc_set_object_visited (target_func_obj_p);
ecma_value_t args_len_or_this = ext_function_p->u.bound_function.args_len_or_this;
if (!ecma_is_value_integer_number (args_len_or_this))
{
if (ecma_is_value_object (args_len_or_this))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (args_len_or_this));
}
return;
}
ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this);
ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1);
JERRY_ASSERT (args_length > 0);
for (ecma_integer_value_t i = 0; i < args_length; i++)
{
if (ecma_is_value_object (args_p[i]))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (args_p[i]));
}
}
} /* ecma_gc_mark_bound_function_object */
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
/**
* Mark objects referenced by Promise built-in.
*/
@@ -315,7 +368,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
if (outer_lex_env_cp != JMEM_CP_NULL)
{
ECMA_GC_SET_OBJECT_VISITED (ECMA_GET_NON_NULL_POINTER (ecma_object_t, outer_lex_env_cp));
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, outer_lex_env_cp));
}
if (ecma_get_lex_env_type (object_p) != ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE)
@@ -332,7 +385,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
if (proto_cp != JMEM_CP_NULL)
{
ECMA_GC_SET_OBJECT_VISITED (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp));
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp));
}
switch (ecma_get_object_type (object_p))
@@ -431,18 +484,16 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
if (ext_object_p->u.array.is_fast_mode)
{
if (object_p->u1.property_list_cp == JMEM_CP_NULL)
if (object_p->u1.property_list_cp != JMEM_CP_NULL)
{
return;
}
ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp);
ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp);
for (uint32_t i = 0; i < ext_object_p->u.array.length; i++)
{
if (ecma_is_value_object (values_p[i]))
for (uint32_t i = 0; i < ext_object_p->u.array.length; i++)
{
ECMA_GC_SET_OBJECT_VISITED (ecma_get_object_from_value (values_p[i]));
if (ecma_is_value_object (values_p[i]))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (values_p[i]));
}
}
}
@@ -452,37 +503,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
}
case ECMA_OBJECT_TYPE_BOUND_FUNCTION:
{
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) object_p;
ecma_object_t *target_func_obj_p;
target_func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ext_function_p->u.bound_function.target_function);
ecma_gc_set_object_visited (target_func_obj_p);
ecma_value_t args_len_or_this = ext_function_p->u.bound_function.args_len_or_this;
if (!ecma_is_value_integer_number (args_len_or_this))
{
if (ecma_is_value_object (args_len_or_this))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (args_len_or_this));
}
break;
}
ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this);
ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1);
JERRY_ASSERT (args_length > 0);
for (ecma_integer_value_t i = 0; i < args_length; i++)
{
if (ecma_is_value_object (args_p[i]))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (args_p[i]));
}
}
ecma_gc_mark_bound_function_object (object_p);
break;
}
case ECMA_OBJECT_TYPE_FUNCTION:
@@ -491,7 +512,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
{
ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p;
ECMA_GC_SET_OBJECT_VISITED (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ext_func_p->u.function.scope_cp));
}
break;
@@ -623,7 +644,7 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
{
JERRY_ASSERT (object_p != NULL
&& !ecma_gc_is_object_visited (object_p)
&& object_p->type_flags_refs < ECMA_OBJECT_REF_ONE);
&& ((object_p->type_flags_refs & ECMA_OBJECT_REF_MASK) == ECMA_OBJECT_NON_VISITED));
bool obj_is_not_lex_env = !ecma_is_lexical_environment (object_p);
@@ -984,6 +1005,10 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
void
ecma_gc_run (void)
{
#if (JERRY_GC_MARK_LIMIT != 0)
JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) == JERRY_GC_MARK_LIMIT);
#endif /* (JERRY_GC_MARK_LIMIT != 0) */
JERRY_CONTEXT (ecma_gc_new_objects) = 0;
ecma_object_t black_list_head;
@@ -1006,7 +1031,7 @@ ecma_gc_run (void)
JERRY_ASSERT (obj_prev_p == NULL
|| ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_prev_p->gc_next_cp) == obj_iter_p);
if (ecma_gc_is_object_visited (obj_iter_p))
if (obj_iter_p->type_flags_refs >= ECMA_OBJECT_REF_ONE)
{
/* Moving the object to list of marked objects. */
obj_prev_p->gc_next_cp = obj_next_cp;
@@ -1016,6 +1041,7 @@ ecma_gc_run (void)
}
else
{
obj_iter_p->type_flags_refs |= ECMA_OBJECT_NON_VISITED;
obj_prev_p = obj_iter_p;
}
@@ -1023,7 +1049,6 @@ ecma_gc_run (void)
}
black_end_p->gc_next_cp = JMEM_CP_NULL;
ecma_object_t *const last_root_object_p = black_end_p;
/* Mark root objects. */
obj_iter_cp = black_list_head.gc_next_cp;
@@ -1039,6 +1064,10 @@ ecma_gc_run (void)
do
{
#if (JERRY_GC_MARK_LIMIT != 0)
JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) == JERRY_GC_MARK_LIMIT);
#endif /* (JERRY_GC_MARK_LIMIT != 0) */
marked_anything_during_current_iteration = false;
obj_prev_p = &white_gray_list_head;
@@ -1060,8 +1089,17 @@ ecma_gc_run (void)
black_end_p->gc_next_cp = obj_iter_cp;
black_end_p = obj_iter_p;
ecma_gc_mark (obj_iter_p);
#if (JERRY_GC_MARK_LIMIT != 0)
if (obj_iter_p->type_flags_refs >= ECMA_OBJECT_REF_ONE)
{
/* Set the reference count of non-marked gray object to 0 */
obj_iter_p->type_flags_refs = (uint16_t) (obj_iter_p->type_flags_refs & (ECMA_OBJECT_REF_ONE - 1));
ecma_gc_mark (obj_iter_p);
marked_anything_during_current_iteration = true;
}
#else /* (JERRY_GC_MARK_LIMIT == 0) */
marked_anything_during_current_iteration = true;
#endif /* (JERRY_GC_MARK_LIMIT != 0) */
}
else
{
@@ -1089,19 +1127,6 @@ ecma_gc_run (void)
obj_iter_cp = obj_next_cp;
}
/* Reset the reference counter of non-root black objects. */
obj_iter_cp = last_root_object_p->gc_next_cp;
while (obj_iter_cp != JMEM_CP_NULL)
{
/* The reference counter must be 1. */
obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp);
ecma_deref_object (obj_iter_p);
JERRY_ASSERT (obj_iter_p->type_flags_refs < ECMA_OBJECT_REF_ONE);
obj_iter_cp = obj_iter_p->gc_next_cp;
}
JERRY_CONTEXT (ecma_gc_objects_cp) = black_list_head.gc_next_cp;
#if ENABLED (JERRY_BUILTIN_REGEXP)
+20 -5
View File
@@ -714,14 +714,29 @@ typedef enum
#endif /* ENABLED (JERRY_DEBUGGER) */
/**
* Value for increasing or decreasing the object reference counter.
* Bitshift index for an ecma-object reference count field
*/
#define ECMA_OBJECT_REF_ONE (1u << 6)
#define ECMA_OBJECT_REF_SHIFT 6
/**
* Maximum value of the object reference counter (1023).
* Bitmask for an ecma-object reference count field
*/
#define ECMA_OBJECT_MAX_REF (0x3ffu << 6)
#define ECMA_OBJECT_REF_MASK (((1u << 10) - 1) << ECMA_OBJECT_REF_SHIFT)
/**
* Value for increasing or decreasing the object reference counter.
*/
#define ECMA_OBJECT_REF_ONE (1u << ECMA_OBJECT_REF_SHIFT)
/**
* Represents non-visited white object
*/
#define ECMA_OBJECT_NON_VISITED (0x3ffu << ECMA_OBJECT_REF_SHIFT)
/**
* Maximum value of the object reference counter (1022).
*/
#define ECMA_OBJECT_MAX_REF (ECMA_OBJECT_NON_VISITED - ECMA_OBJECT_REF_ONE)
/**
* Description of ECMA-object or lexical environment
@@ -733,7 +748,7 @@ typedef struct
depending on ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV
flags : 2 bit : ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV,
ECMA_OBJECT_FLAG_EXTENSIBLE or ECMA_OBJECT_FLAG_NON_CLOSURE
refs : 10 bit (max 1023) */
refs : 10 bit (max 1022) */
uint16_t type_flags_refs;
/** next in the object chain maintained by the garbage collector */
+2 -2
View File
@@ -54,8 +54,8 @@ JERRY_STATIC_ASSERT (ECMA_OBJECT_FLAG_EXTENSIBLE == (ECMA_OBJECT_FLAG_BUILT_IN_O
JERRY_STATIC_ASSERT (ECMA_OBJECT_REF_ONE == (ECMA_OBJECT_FLAG_EXTENSIBLE << 1),
ecma_object_ref_one_must_follow_the_extensible_flag);
JERRY_STATIC_ASSERT ((ECMA_OBJECT_MAX_REF | (ECMA_OBJECT_REF_ONE - 1)) == UINT16_MAX,
ecma_object_max_ref_does_not_fill_the_remaining_bits);
JERRY_STATIC_ASSERT (((ECMA_OBJECT_MAX_REF + ECMA_OBJECT_REF_ONE) | (ECMA_OBJECT_REF_ONE - 1)) == UINT16_MAX,
ecma_object_max_ref_does_not_fill_the_remaining_bits);
JERRY_STATIC_ASSERT (ECMA_PROPERTY_TYPE_DELETED == (ECMA_DIRECT_STRING_MAGIC << ECMA_PROPERTY_NAME_TYPE_SHIFT),
ecma_property_type_deleted_must_have_magic_string_name_type);
@@ -47,6 +47,10 @@ ecma_init (void)
JERRY_CONTEXT (stack_base) = (uintptr_t)&sp;
#endif /* (JERRY_STACK_LIMIT != 0) */
#if (JERRY_GC_MARK_LIMIT != 0)
JERRY_CONTEXT (ecma_gc_mark_recursion_limit) = JERRY_GC_MARK_LIMIT;
#endif /* (JERRY_GC_MARK_LIMIT != 0) */
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
ecma_job_queue_init ();
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
+3
View File
@@ -159,6 +159,9 @@ struct jerry_context_t
uint32_t lit_magic_string_ex_count; /**< external magic strings count */
uint32_t jerry_init_flags; /**< run-time configuration flags */
uint32_t status_flags; /**< run-time flags (the top 8 bits are used for passing class parsing options) */
#if (JERRY_GC_MARK_LIMIT != 0)
uint32_t ecma_gc_mark_recursion_limit; /**< GC mark recursion limit */
#endif /* (JERRY_GC_MARK_LIMIT != 0) */
#if ENABLED (JERRY_PROPRETY_HASHMAP)
uint8_t ecma_prop_hashmap_alloc_state; /**< property hashmap allocation state: 0-4,