mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Using hash value stored in ecma-string for lcache search; comparing string faster but with possible false negatives while performing lcache lookup.
This commit is contained in:
parent
fe7e34d93e
commit
3b29280b79
@ -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;
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
/**
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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 */
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user