Use run-time cache to keep position of dynamic properties in HashTable to access them without hash lookup.

This commit is contained in:
Dmitry Stogov 2017-10-23 18:29:13 +03:00
parent 0eeaff09cb
commit 193adbdf30
4 changed files with 1207 additions and 455 deletions

View File

@ -36,7 +36,6 @@
#define DEBUG_OBJECT_HANDLERS 0
#define ZEND_WRONG_PROPERTY_OFFSET 0
#define ZEND_DYNAMIC_PROPERTY_OFFSET ((uint32_t)(-1))
/* guard flags */
#define IN_GET (1<<0)
@ -578,8 +577,33 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_
}
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
if (EXPECTED(zobj->properties != NULL)) {
if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(property_offset)) {
uint32_t idx = ZEND_DECODE_DYN_PROP_OFFSET(property_offset);
if (EXPECTED(idx < zobj->properties->nNumUsed)) {
Bucket *p = zobj->properties->arData + idx;
if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
(EXPECTED(p->key == Z_STR_P(member)) ||
(EXPECTED(p->h == ZSTR_H(Z_STR_P(member))) &&
EXPECTED(p->key != NULL) &&
EXPECTED(ZSTR_LEN(p->key) == Z_STRLEN_P(member)) &&
EXPECTED(memcmp(ZSTR_VAL(p->key), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0)))) {
retval = &p->val;
goto exit;
}
}
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
}
retval = zend_hash_find(zobj->properties, Z_STR_P(member));
if (EXPECTED(retval)) goto exit;
if (EXPECTED(retval)) {
if (cache_slot) {
uint32_t idx = ((char*)retval - (char*)zobj->properties->arData) / sizeof(Bucket);
/* Store "hash slot index" + 2 (NULL is a mark of uninitialized cache slot) */
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
}
goto exit;
}
}
} else if (UNEXPECTED(EG(exception))) {
retval = &EG(uninitialized_zval);
@ -1522,22 +1546,47 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists,
goto found;
}
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
if (EXPECTED(zobj->properties != NULL) &&
(value = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) {
found:
switch (has_set_exists) {
case 0:
ZVAL_DEREF(value);
result = (Z_TYPE_P(value) != IS_NULL);
break;
default:
result = zend_is_true(value);
break;
case 2:
result = 1;
break;
if (EXPECTED(zobj->properties != NULL)) {
if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(property_offset)) {
uint32_t idx = ZEND_DECODE_DYN_PROP_OFFSET(property_offset);
if (EXPECTED(idx < zobj->properties->nNumUsed)) {
Bucket *p = zobj->properties->arData + idx;
if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
(EXPECTED(p->key == Z_STR_P(member)) ||
(EXPECTED(p->h == ZSTR_H(Z_STR_P(member))) &&
EXPECTED(p->key != NULL) &&
EXPECTED(ZSTR_LEN(p->key) == Z_STRLEN_P(member)) &&
EXPECTED(memcmp(ZSTR_VAL(p->key), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0)))) {
value = &p->val;
goto found;
}
}
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
}
value = zend_hash_find(zobj->properties, Z_STR_P(member));
if (value) {
if (cache_slot) {
uint32_t idx = ((char*)value - (char*)zobj->properties->arData) / sizeof(Bucket);
/* Store "hash slot index" + 2 (NULL is a mark of uninitialized cache slot) */
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
}
found:
switch (has_set_exists) {
case 0:
ZVAL_DEREF(value);
result = (Z_TYPE_P(value) != IS_NULL);
break;
default:
result = zend_is_true(value);
break;
case 2:
result = 1;
break;
}
goto exit;
}
goto exit;
}
} else if (UNEXPECTED(EG(exception))) {
result = 0;

View File

@ -27,9 +27,16 @@ struct _zend_property_info;
#define ZEND_WRONG_PROPERTY_INFO \
((struct _zend_property_info*)((zend_intptr_t)-1))
#define IS_VALID_PROPERTY_OFFSET(offset) ((int32_t)(offset) > 0)
#define IS_WRONG_PROPERTY_OFFSET(offset) ((int32_t)(offset) == 0)
#define IS_DYNAMIC_PROPERTY_OFFSET(offset) ((int32_t)(offset) < 0)
#define ZEND_DYNAMIC_PROPERTY_OFFSET ((uint32_t)(-1))
#define IS_VALID_PROPERTY_OFFSET(offset) ((int32_t)(offset) > 0)
#define IS_WRONG_PROPERTY_OFFSET(offset) ((int32_t)(offset) == 0)
#define IS_DYNAMIC_PROPERTY_OFFSET(offset) ((int32_t)(offset) < 0)
#define IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(offset) (offset == ZEND_DYNAMIC_PROPERTY_OFFSET)
#define ZEND_DECODE_DYN_PROP_OFFSET(offset) ((uint32_t)(-(int32_t)(offset) - 2))
#define ZEND_ENCODE_DYN_PROP_OFFSET(idx) ((uint32_t)(-(idx + 2)))
/* The following rule applies to read_property() and read_dimension() implementations:
If you return a zval which is not otherwise referenced by the extension or the engine's

View File

@ -1760,6 +1760,7 @@ ZEND_VM_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMP|VAR|UNUSED|THIS|CV, CONST|TMPVAR
zval *container;
zend_free_op free_op2;
zval *offset;
void **cache_slot = NULL;
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R);
@ -1787,21 +1788,44 @@ ZEND_VM_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMP|VAR|UNUSED|THIS|CV, CONST|TMPVAR
zend_object *zobj = Z_OBJ_P(container);
zval *retval;
if (OP2_TYPE == IS_CONST &&
EXPECTED(zobj->ce == CACHED_PTR(Z_CACHE_SLOT_P(offset)))) {
uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(offset) + sizeof(void*));
if (OP2_TYPE == IS_CONST) {
cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(offset));
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
retval = OBJ_PROP(zobj, prop_offset);
if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) {
ZVAL_COPY_UNREF(EX_VAR(opline->result.var), retval);
break;
}
} else if (EXPECTED(zobj->properties != NULL)) {
retval = zend_hash_find(zobj->properties, Z_STR_P(offset));
if (EXPECTED(retval)) {
ZVAL_COPY_UNREF(EX_VAR(opline->result.var), retval);
break;
if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) {
uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
retval = OBJ_PROP(zobj, prop_offset);
if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) {
ZVAL_COPY_UNREF(EX_VAR(opline->result.var), retval);
break;
}
} else if (EXPECTED(zobj->properties != NULL)) {
if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
uint32_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);
if (EXPECTED(idx < zobj->properties->nNumUsed)) {
Bucket *p = zobj->properties->arData + idx;
if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
(EXPECTED(p->key == Z_STR_P(offset)) ||
(EXPECTED(p->h == ZSTR_H(Z_STR_P(offset))) &&
EXPECTED(p->key != NULL) &&
EXPECTED(ZSTR_LEN(p->key) == Z_STRLEN_P(offset)) &&
EXPECTED(memcmp(ZSTR_VAL(p->key), Z_STRVAL_P(offset), Z_STRLEN_P(offset)) == 0)))) {
ZVAL_COPY_UNREF(EX_VAR(opline->result.var), &p->val);
break;
}
}
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
}
retval = zend_hash_find(zobj->properties, Z_STR_P(offset));
if (EXPECTED(retval)) {
uint32_t idx = ((char*)retval - (char*)zobj->properties->arData) / sizeof(Bucket);
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
ZVAL_COPY_UNREF(EX_VAR(opline->result.var), retval);
break;
}
}
}
}
@ -1814,7 +1838,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_no_object):
zend_string_release(property_name);
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
retval = zobj->handlers->read_property(container, offset, BP_VAR_R, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL), EX_VAR(opline->result.var));
retval = zobj->handlers->read_property(container, offset, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
if (retval != EX_VAR(opline->result.var)) {
ZVAL_COPY_UNREF(EX_VAR(opline->result.var), retval);
@ -1881,6 +1905,7 @@ ZEND_VM_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR
zval *container;
zend_free_op free_op2;
zval *offset;
void **cache_slot = NULL;
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS);
@ -1908,21 +1933,44 @@ ZEND_VM_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR
zend_object *zobj = Z_OBJ_P(container);
zval *retval;
if (OP2_TYPE == IS_CONST &&
EXPECTED(zobj->ce == CACHED_PTR(Z_CACHE_SLOT_P(offset)))) {
uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(offset) + sizeof(void*));
if (OP2_TYPE == IS_CONST) {
cache_slot = CACHE_ADDR(Z_CACHE_SLOT_P(offset));
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
retval = OBJ_PROP(zobj, prop_offset);
if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
ZVAL_COPY(EX_VAR(opline->result.var), retval);
break;
}
} else if (EXPECTED(zobj->properties != NULL)) {
retval = zend_hash_find(zobj->properties, Z_STR_P(offset));
if (EXPECTED(retval)) {
ZVAL_COPY(EX_VAR(opline->result.var), retval);
break;
if (EXPECTED(zobj->ce == CACHED_PTR_EX(cache_slot))) {
uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
retval = OBJ_PROP(zobj, prop_offset);
if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
ZVAL_COPY(EX_VAR(opline->result.var), retval);
break;
}
} else if (EXPECTED(zobj->properties != NULL)) {
if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
uint32_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);
if (EXPECTED(idx < zobj->properties->nNumUsed)) {
Bucket *p = zobj->properties->arData + idx;
if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
(EXPECTED(p->key == Z_STR_P(offset)) ||
(EXPECTED(p->h == ZSTR_H(Z_STR_P(offset))) &&
EXPECTED(p->key != NULL) &&
EXPECTED(ZSTR_LEN(p->key) == Z_STRLEN_P(offset)) &&
EXPECTED(memcmp(ZSTR_VAL(p->key), Z_STRVAL_P(offset), Z_STRLEN_P(offset)) == 0)))) {
ZVAL_COPY(EX_VAR(opline->result.var), &p->val);
break;
}
}
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
}
retval = zend_hash_find(zobj->properties, Z_STR_P(offset));
if (EXPECTED(retval)) {
uint32_t idx = ((char*)retval - (char*)zobj->properties->arData) / sizeof(Bucket);
CACHE_PTR_EX(cache_slot + 1, (void*)(uintptr_t)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
ZVAL_COPY(EX_VAR(opline->result.var), retval);
break;
}
}
}
}
@ -1932,7 +1980,7 @@ ZEND_VM_C_LABEL(fetch_obj_is_no_object):
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
retval = zobj->handlers->read_property(container, offset, BP_VAR_IS, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL), EX_VAR(opline->result.var));
retval = zobj->handlers->read_property(container, offset, BP_VAR_IS, cache_slot, EX_VAR(opline->result.var));
if (retval != EX_VAR(opline->result.var)) {
ZVAL_COPY(EX_VAR(opline->result.var), retval);

File diff suppressed because it is too large Load Diff