Used string with biased hash for double deduplication

Rather than using a separate hash table for doubles, use the same
biases hash approach we use for everything else. Either way works
fine, but there doesn't seem to be any strong reason to use a
different approach for doubles than we use for other cases.
This commit is contained in:
Nikita Popov 2022-05-21 21:58:06 +02:00
parent 83e0a5d2a7
commit cc506a81e1

View file

@ -100,6 +100,13 @@ static uint32_t add_static_slot(HashTable *hash,
return ret; return ret;
} }
static inline void bias_key(zend_string *key, uint32_t bias)
{
/* Add a bias to the hash so we can distinguish string keys
* that would otherwise be the same. */
ZSTR_H(key) = zend_string_hash_val(key) + bias;
}
static zend_string *create_str_cache_key(zval *literal, uint8_t num_related) static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
{ {
ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING); ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
@ -124,9 +131,7 @@ static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
ZEND_ASSERT(0 && "Currently not needed"); ZEND_ASSERT(0 && "Currently not needed");
} }
/* Add a bias to the hash so we can distinguish keys bias_key(key, num_related - 1);
* that would otherwise be the same after concatenation. */
ZSTR_H(key) = zend_string_hash_val(key) + num_related - 1;
return key; return key;
} }
@ -141,7 +146,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
int l_false = -1; int l_false = -1;
int l_true = -1; int l_true = -1;
int l_empty_arr = -1; int l_empty_arr = -1;
HashTable hash, double_hash; HashTable hash;
zend_string *key = NULL; zend_string *key = NULL;
void *checkpoint = zend_arena_checkpoint(ctx->arena); void *checkpoint = zend_arena_checkpoint(ctx->arena);
int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot; int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
@ -285,8 +290,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
/* Merge equal constants */ /* Merge equal constants */
j = 0; j = 0;
zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0); zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
/* Use separate hashtable for doubles stored as string keys, to avoid collisions. */
zend_hash_init(&double_hash, 0, NULL, NULL, 0);
map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int)); map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
memset(map, 0, op_array->last_literal * sizeof(int)); memset(map, 0, op_array->last_literal * sizeof(int));
for (i = 0; i < op_array->last_literal; i++) { for (i = 0; i < op_array->last_literal; i++) {
@ -349,10 +352,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
} else { } else {
ZEND_ASSERT(info[i].num_related == 2); ZEND_ASSERT(info[i].num_related == 2);
key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0); key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 + bias_key(key, 100 + info[i].num_related - 1);
info[i].num_related - 1; if ((pos = zend_hash_find(&hash, key)) != NULL) {
if ((pos = zend_hash_find(&hash, key)) != NULL ZEND_ASSERT(info[Z_LVAL_P(pos)].num_related == 2);
&& info[Z_LVAL_P(pos)].num_related == 2) {
map[i] = Z_LVAL_P(pos); map[i] = Z_LVAL_P(pos);
zval_ptr_dtor_nogc(&op_array->literals[i+1]); zval_ptr_dtor_nogc(&op_array->literals[i+1]);
} else { } else {
@ -373,18 +375,21 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
break; break;
case IS_DOUBLE: case IS_DOUBLE:
ZEND_ASSERT(info[i].num_related == 1); ZEND_ASSERT(info[i].num_related == 1);
if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) { key = zend_string_init((char*)&Z_DVAL(op_array->literals[i]), sizeof(double), 0);
bias_key(key, 200);
if ((pos = zend_hash_find(&hash, key))) {
map[i] = Z_LVAL_P(pos); map[i] = Z_LVAL_P(pos);
} else { } else {
map[i] = j; map[i] = j;
ZVAL_LONG(&zv, j); ZVAL_LONG(&zv, j);
zend_hash_str_add_new(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv); zend_hash_add_new(&hash, key, &zv);
if (i != j) { if (i != j) {
op_array->literals[j] = op_array->literals[i]; op_array->literals[j] = op_array->literals[i];
info[j] = info[i]; info[j] = info[i];
} }
j++; j++;
} }
zend_string_release_ex(key, 0);
break; break;
case IS_STRING: { case IS_STRING: {
key = create_str_cache_key(&op_array->literals[i], info[i].num_related); key = create_str_cache_key(&op_array->literals[i], info[i].num_related);
@ -452,7 +457,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
/* Only clean "hash", as it will be reused in the loop below. */ /* Only clean "hash", as it will be reused in the loop below. */
zend_hash_clean(&hash); zend_hash_clean(&hash);
zend_hash_destroy(&double_hash);
op_array->last_literal = j; op_array->last_literal = j;
const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int)); const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));