diff --git a/src/libecmaobjects/ecma-globals.h b/src/libecmaobjects/ecma-globals.h index 8ce82b2a3..a942c06d6 100644 --- a/src/libecmaobjects/ecma-globals.h +++ b/src/libecmaobjects/ecma-globals.h @@ -743,7 +743,7 @@ typedef enum /** * ECMA string hash */ -typedef uint16_t ecma_string_hash_t; +typedef uint8_t ecma_string_hash_t; /** * Number of string's last characters to use for hash calculation @@ -758,13 +758,13 @@ typedef struct /** Reference counter for the string */ unsigned int refs : CONFIG_ECMA_REFERENCE_COUNTER_WIDTH; - /** Where the string's data is placed (ecma_string_container_t) */ - unsigned int container : 3; - /** Flag indicating whether the string descriptor is placed * in a stack variable (not in the heap) */ unsigned int is_stack_var : 1; + /** Where the string's data is placed (ecma_string_container_t) */ + uint8_t container; + /** Hash of the string (calculated from two last characters of the string) */ ecma_string_hash_t hash; @@ -794,6 +794,9 @@ typedef struct /** Identifier of magic string */ ecma_magic_string_id_t magic_string_id; + + /** For zeroing and comparison in some cases */ + uint32_t common_field; } u; } ecma_string_t; diff --git a/src/libecmaobjects/ecma-helpers-string.c b/src/libecmaobjects/ecma-helpers-string.c index 72d583259..a0e3cd92b 100644 --- a/src/libecmaobjects/ecma-helpers-string.c +++ b/src/libecmaobjects/ecma-helpers-string.c @@ -308,6 +308,7 @@ ecma_init_ecma_string_from_lit_index (ecma_string_t *string_p, /**< descriptor t string_p->container = ECMA_STRING_CONTAINER_LIT_TABLE; string_p->hash = lit.data.lp.hash; + string_p->u.common_field = 0; string_p->u.lit_index = lit_index; } /* ecma_init_ecma_string_from_lit_index */ @@ -331,6 +332,7 @@ ecma_init_ecma_string_from_magic_string_id (ecma_string_t *string_p, /**< descri string_p->hash = ecma_chars_buffer_calc_hash_last_chars (ecma_get_magic_string_zt (magic_string_id), ecma_magic_string_lengths [magic_string_id]); + string_p->u.common_field = 0; string_p->u.magic_string_id = magic_string_id; } /* ecma_init_ecma_string_from_magic_string_id */ @@ -366,6 +368,7 @@ ecma_new_ecma_string (const ecma_char_t *string_p) /**< zero-terminated string * string_desc_p->container = ECMA_STRING_CONTAINER_HEAP_CHUNKS; string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (string_p, length); + string_desc_p->u.common_field = 0; ecma_collection_header_t *collection_p = ecma_new_chars_collection (string_p, length); ECMA_SET_NON_NULL_POINTER (string_desc_p->u.collection_cp, collection_p); @@ -384,7 +387,6 @@ ecma_new_ecma_string_from_uint32 (uint32_t uint32_number) /**< UInt32-represente string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_UINT32_IN_DESC; - string_desc_p->u.uint32_number = uint32_number; uint32_t last_two_digits = uint32_number % 100; uint32_t digit_pl = last_two_digits / 10; @@ -414,6 +416,9 @@ ecma_new_ecma_string_from_uint32 (uint32_t uint32_number) /**< UInt32-represente ecma_zt_string_length (char_buf))); #endif /* !JERRY_NDEBUG */ + string_desc_p->u.common_field = 0; + string_desc_p->u.uint32_number = uint32_number; + return string_desc_p; } /* ecma_new_ecma_string_from_uint32 */ @@ -442,6 +447,7 @@ ecma_new_ecma_string_from_number (ecma_number_t num) /**< ecma-number */ string_desc_p->container = ECMA_STRING_CONTAINER_HEAP_NUMBER; string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (str_buf, length); + string_desc_p->u.common_field = 0; ecma_number_t *num_p = ecma_alloc_number (); *num_p = num; ECMA_SET_POINTER (string_desc_p->u.number_cp, num_p); @@ -529,15 +535,17 @@ ecma_concat_ecma_strings (ecma_string_t *string1_p, /**< first ecma-string */ int64_t length = (int64_t) str1_len + (int64_t) str2_len; + if (length > ECMA_STRING_MAX_CONCATENATION_LENGTH) + { + jerry_exit (ERR_OUT_OF_MEMORY); + } + ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_CONCATENATION; - if (length > ECMA_STRING_MAX_CONCATENATION_LENGTH) - { - jerry_exit (ERR_OUT_OF_MEMORY); - } + string_desc_p->u.common_field = 0; string1_p = ecma_copy_or_ref_ecma_string (string1_p); string2_p = ecma_copy_or_ref_ecma_string (string2_p); @@ -954,6 +962,28 @@ static bool __noinline ecma_compare_ecma_strings_longpath (const ecma_string_t *string1_p, /* ecma-string */ const ecma_string_t *string2_p) /* ecma-string */ { + if (string1_p->container == string2_p->container) + { + if (string1_p->container == ECMA_STRING_CONTAINER_LIT_TABLE) + { + JERRY_ASSERT (string1_p->u.lit_index != string2_p->u.lit_index); + + return false; + } + else if (string1_p->container == ECMA_STRING_CONTAINER_MAGIC_STRING) + { + JERRY_ASSERT (string1_p->u.magic_string_id != string2_p->u.magic_string_id); + + return false; + } + else if (string1_p->container == ECMA_STRING_CONTAINER_UINT32_IN_DESC) + { + JERRY_ASSERT (string1_p->u.uint32_number != string2_p->u.uint32_number); + + return false; + } + } + const int32_t string1_len = ecma_string_get_length (string1_p); const int32_t string2_len = ecma_string_get_length (string2_p); @@ -1000,10 +1030,22 @@ ecma_compare_ecma_strings_longpath (const ecma_string_t *string1_p, /* ecma-stri break; } case ECMA_STRING_CONTAINER_LIT_TABLE: + { + JERRY_ASSERT (string1_p->u.lit_index != string2_p->u.lit_index); + + return false; + } case ECMA_STRING_CONTAINER_MAGIC_STRING: + { + JERRY_ASSERT (string1_p->u.magic_string_id != string2_p->u.magic_string_id); + + return false; + } case ECMA_STRING_CONTAINER_UINT32_IN_DESC: { - JERRY_UNREACHABLE (); + JERRY_ASSERT (string1_p->u.uint32_number != string2_p->u.uint32_number); + + return false; } } } @@ -1036,6 +1078,29 @@ ecma_compare_ecma_strings_longpath (const ecma_string_t *string1_p, /* ecma-stri return is_equal; } /* ecma_compare_ecma_strings_longpath */ +/** + * Compare ecma-string to ecma-string if they're hashes are equal + * + * @return true - if strings are equal; + * false - may be. + */ +bool +ecma_compare_ecma_strings_equal_hashes (const ecma_string_t *string1_p, /* ecma-string */ + const ecma_string_t *string2_p) /* ecma-string */ +{ + JERRY_ASSERT (string1_p->hash == string2_p->hash); + + if (string1_p->container == string2_p->container + && string1_p->u.common_field == string2_p->u.common_field) + { + return true; + } + else + { + return false; + } +} /* ecma_compare_ecma_strings_equal_hashes */ + /** * Compare ecma-string to ecma-string * @@ -1048,28 +1113,23 @@ ecma_compare_ecma_strings (const ecma_string_t *string1_p, /* ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); - if (string1_p->hash != string2_p->hash) + const bool is_equal_hashes = (string1_p->hash == string2_p->hash); + + if (!is_equal_hashes) { return false; } + const bool is_equal_containers = (string1_p->container == string2_p->container); + const bool is_equal_fields = (string1_p->u.common_field == string2_p->u.common_field); - if (string1_p->container == string2_p->container) + if (is_equal_containers && is_equal_fields) { - if (likely (string1_p->container == ECMA_STRING_CONTAINER_LIT_TABLE)) - { - return (string1_p->u.lit_index == string2_p->u.lit_index); - } - else if (likely (string1_p->container == ECMA_STRING_CONTAINER_MAGIC_STRING)) - { - return (string1_p->u.magic_string_id == string2_p->u.magic_string_id); - } - else if (string1_p->container == ECMA_STRING_CONTAINER_UINT32_IN_DESC) - { - return (string1_p->u.uint32_number == string2_p->u.uint32_number); - } + return true; + } + else + { + return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } - - return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } /* ecma_compare_ecma_strings */ /** @@ -1614,39 +1674,13 @@ ecma_is_string_magic (const ecma_string_t *string_p, /**< ecma-string */ /** * Try to calculate hash of the ecma-string * - * Note: - * Not all ecma-string containers provide ability to calculate hash. - * For now, only strings stored in literal table support calculating of hash. - * - * Warning: - * Anyway, if ecma-string is hashable, for given length of hash, - * the hash values should always be equal for equal strings - * irrespective of the strings' container type. - * - * If a ecma-string stored in a container of some type is hashable, - * then the ecma-string should always be hashable if stored in container - * of the same type. - * - * @return true - if hash was calculated, - * false - otherwise (the string is not hashable). + * @return calculated hash */ -bool -ecma_string_try_hash (const ecma_string_t *string_p, /**< ecma-string to calculate hash for */ - uint32_t hash_length_bits, /**< length of hash value, in bits */ - uint32_t *out_hash_p) /**< out: hash value, if calculated */ +ecma_string_hash_t +ecma_string_hash (const ecma_string_t *string_p) /**< ecma-string to calculate hash for */ { - JERRY_ASSERT (hash_length_bits < sizeof (uint32_t) * JERRY_BITSINBYTE); - uint32_t hash_mask = ((1u << hash_length_bits) - 1); - - if (string_p->container == ECMA_STRING_CONTAINER_LIT_TABLE) - { - *out_hash_p = (string_p->u.lit_index) & hash_mask; - - return true; - } - - return false; + return (string_p->hash); } /* ecma_string_try_hash */ /** @@ -1666,8 +1700,9 @@ ecma_chars_buffer_calc_hash_last_chars (const ecma_char_t *chars, /**< character uint32_t t1 = (uint32_t) char1 + (uint32_t) char2; uint32_t t2 = t1 * 0x24418b66; uint32_t t3 = (t2 >> 16) ^ (t2 & 0xffffu); + uint32_t t4 = (t3 >> 8) ^ (t3 & 0xffu); - return (ecma_string_hash_t) t3; + return (ecma_string_hash_t) t4; } /* ecma_chars_buffer_calc_hash_last_chars */ /** diff --git a/src/libecmaobjects/ecma-helpers.h b/src/libecmaobjects/ecma-helpers.h index 58f9a27a5..037d72204 100644 --- a/src/libecmaobjects/ecma-helpers.h +++ b/src/libecmaobjects/ecma-helpers.h @@ -137,6 +137,8 @@ extern ecma_number_t ecma_string_to_number (const ecma_string_t *str_p); extern ssize_t ecma_string_to_zt_string (const ecma_string_t *string_desc_p, ecma_char_t *buffer_p, ssize_t buffer_size); +extern bool ecma_compare_ecma_strings_equal_hashes (const ecma_string_t *string1_p, + const ecma_string_t *string2_p); extern bool ecma_compare_ecma_strings (const ecma_string_t *string1_p, const ecma_string_t *string2_p); extern bool ecma_compare_ecma_strings_relational (const ecma_string_t *string1_p, @@ -156,7 +158,7 @@ extern const ecma_char_t* ecma_get_magic_string_zt (ecma_magic_string_id_t id); extern ecma_string_t* ecma_get_magic_string (ecma_magic_string_id_t id); extern bool ecma_is_string_magic (const ecma_string_t *string_p, ecma_magic_string_id_t *out_id_p); extern bool ecma_is_zt_string_magic (const ecma_char_t *zt_string_p, ecma_magic_string_id_t *out_id_p); -extern bool ecma_string_try_hash (const ecma_string_t *string_p, uint32_t hash_length_bits, uint32_t *out_hash_p); +extern ecma_string_hash_t ecma_string_hash (const ecma_string_t *string_p); extern ecma_string_hash_t ecma_chars_buffer_calc_hash_last_chars (const ecma_char_t *chars, ecma_length_t length); /* ecma-helpers-number.c */ diff --git a/src/libecmaobjects/ecma-lcache.c b/src/libecmaobjects/ecma-lcache.c index 286392005..05d693278 100644 --- a/src/libecmaobjects/ecma-lcache.c +++ b/src/libecmaobjects/ecma-lcache.c @@ -44,7 +44,7 @@ typedef struct /** * LCache hash value length, in bits */ -#define ECMA_LCACHE_HASH_BITS (8) +#define ECMA_LCACHE_HASH_BITS (sizeof (ecma_string_hash_t) * JERRY_BITSINBYTE) /** * Number of rows in LCache's hash table @@ -142,15 +142,28 @@ ecma_lcache_insert (ecma_object_t *object_p, /**< object */ JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); - uint32_t hash_key; - - if (!ecma_string_try_hash (prop_name_p, ECMA_LCACHE_HASH_BITS, &hash_key)) - { - return; - } + ecma_string_hash_t hash_key = ecma_string_hash (prop_name_p); if (prop_p != NULL) { + if (unlikely (ecma_is_property_lcached (prop_p))) + { + uint16_t prop_cp; + ECMA_SET_NON_NULL_POINTER (prop_cp, prop_p); + + int32_t entry_index; + for (entry_index = 0; entry_index < ECMA_LCACHE_HASH_ROW_LENGTH; entry_index++) + { + if (ecma_lcache_hash_table[hash_key][entry_index].prop_cp == prop_cp) + { + break; + } + } + + JERRY_ASSERT (entry_index != ECMA_LCACHE_HASH_ROW_LENGTH); + ecma_lcache_invalidate_entry (&ecma_lcache_hash_table[hash_key][entry_index]); + } + JERRY_ASSERT (!ecma_is_property_lcached (prop_p)); ecma_set_property_lcached (prop_p, true); } @@ -186,7 +199,7 @@ ecma_lcache_insert (ecma_object_t *object_p, /**< object */ * Lookup property in the LCache * * @return true - if (object, property name) pair is registered in LCache, - * false - otherwise. + * false - probably, not registered. */ bool ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ @@ -198,28 +211,30 @@ ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ * if return value is false, * then the output parameter is not set */ { - uint32_t hash_key; - - if (!ecma_string_try_hash (prop_name_p, ECMA_LCACHE_HASH_BITS, &hash_key)) - { - return false; - } + ecma_string_hash_t hash_key = ecma_string_hash (prop_name_p); unsigned int object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); for (uint32_t i = 0; i < ECMA_LCACHE_HASH_ROW_LENGTH; i++) { - if (ecma_lcache_hash_table[hash_key][i].object_cp == object_cp - && ecma_compare_ecma_strings (prop_name_p, - ECMA_GET_NON_NULL_POINTER (ecma_lcache_hash_table[hash_key][i].prop_name_cp))) + if (ecma_lcache_hash_table[hash_key][i].object_cp == object_cp) { - ecma_property_t *prop_p = ECMA_GET_POINTER (ecma_lcache_hash_table[hash_key][i].prop_cp); - JERRY_ASSERT (prop_p == NULL || ecma_is_property_lcached (prop_p)); + ecma_string_t *entry_prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_lcache_hash_table[hash_key][i].prop_name_cp); - *prop_p_p = prop_p; + if (ecma_compare_ecma_strings_equal_hashes (prop_name_p, entry_prop_name_p)) + { + ecma_property_t *prop_p = ECMA_GET_POINTER (ecma_lcache_hash_table[hash_key][i].prop_cp); + JERRY_ASSERT (prop_p == NULL || ecma_is_property_lcached (prop_p)); - return true; + *prop_p_p = prop_p; + + return true; + } + else + { + /* may be equal but it is long to compare it here */ + } } } @@ -277,23 +292,12 @@ ecma_lcache_invalidate (ecma_object_t *object_p, /**< object */ ECMA_SET_NON_NULL_POINTER (object_cp, object_p); ECMA_SET_POINTER (prop_cp, prop_p); - uint32_t hash_key; + ecma_string_hash_t hash_key = ecma_string_hash (prop_name_p); - if (!ecma_string_try_hash (prop_name_p, ECMA_LCACHE_HASH_BITS, &hash_key)) - { - /* Property's name hash was not computed, so iterating the whole hash table */ - for (uint32_t row_index = 0; row_index < ECMA_LCACHE_HASH_ROWS_COUNT; row_index++) - { - ecma_lcache_invalidate_row_for_object_property_pair (row_index, object_cp, prop_cp); - } - } - else - { - /* Property's name has was computed. - * Given (object, property name) pair should be in the row corresponding to computed hash. - */ - ecma_lcache_invalidate_row_for_object_property_pair (hash_key, object_cp, prop_cp); - } + /* Property's name has was computed. + * Given (object, property name) pair should be in the row corresponding to computed hash. + */ + ecma_lcache_invalidate_row_for_object_property_pair (hash_key, object_cp, prop_cp); } /* ecma_lcache_invalidate */ /**