Implement constant expression folding as a separate pass

This commit is contained in:
Nikita Popov 2014-06-23 21:30:57 +02:00
parent 0644fbccf7
commit e26c6d663a
4 changed files with 109 additions and 127 deletions

View File

@ -411,7 +411,7 @@ ZEND_API void zend_ast_destroy(zend_ast *ast)
int i;
if (ast->kind == ZEND_CONST) {
zval_dtor(zend_ast_get_zval(ast));
zval_ptr_dtor(zend_ast_get_zval(ast));
} else if (ast->kind != ZEND_AST_ZNODE) {
for (i = 0; i < ast->children; i++) {
if (ast->child[i]) {

View File

@ -155,6 +155,7 @@ static inline zend_ast *zend_ast_create_assign_op(zend_uint opcode, zend_ast *op
/* Temporary, for porting */
#define AST_COMPILE(res, ast) do { \
zend_ast *_ast = (ast); \
zend_eval_const_expr(&_ast TSRMLS_CC); \
zend_compile_expr((res), _ast TSRMLS_CC); \
zend_ast_destroy(_ast); \
} while (0)

View File

@ -7182,31 +7182,16 @@ void zend_compile_unset(zend_ast *ast TSRMLS_DC) {
}
}
int zend_compile_binary_op_maybe_ct(znode *result, zend_ast *ast, zend_bool ct_required TSRMLS_DC) {
void zend_compile_binary_op(znode *result, zend_ast *ast TSRMLS_DC) {
zend_ast *left_ast = ast->child[0];
zend_ast *right_ast = ast->child[1];
zend_uint opcode = ast->attr;
znode left_node, right_node;
int ct_left = zend_compile_expr_maybe_ct(&left_node, left_ast, ct_required TSRMLS_CC);
int ct_right = zend_compile_expr_maybe_ct(&right_node, right_ast, ct_required TSRMLS_CC);
if (ct_left == SUCCESS && ct_right == SUCCESS) {
binary_op_type op = get_binary_op(opcode);
op(&result->u.constant, &left_node.u.constant, &right_node.u.constant TSRMLS_CC);
zval_ptr_dtor(&left_node.u.constant);
zval_ptr_dtor(&right_node.u.constant);
result->op_type = IS_CONST;
return SUCCESS;
} else if (ct_required) {
if (ct_left == SUCCESS) zval_ptr_dtor(&left_node.u.constant);
if (ct_right == SUCCESS) zval_ptr_dtor(&right_node.u.constant);
return FAILURE;
}
zend_compile_expr(&left_node, left_ast TSRMLS_CC);
zend_compile_expr(&right_node, right_ast TSRMLS_CC);
emit_op_tmp(result, opcode, &left_node, &right_node TSRMLS_CC);
return FAILURE;
}
/* We do not use zend_compile_binary_op for this because we want to retain the left-to-right
@ -7590,96 +7575,11 @@ void zend_compile_shell_exec(znode *result, zend_ast *ast TSRMLS_DC) {
zend_ast_destroy(name_ast);
}
int zend_try_ct_compile_array(zval *array, zend_ast *ast TSRMLS_DC) {
zend_uint i;
ZVAL_UNDEF(array);
for (i = 0; i < ast->children; ++i) {
zend_ast *elem_ast = ast->child[i];
zend_ast *value_ast = elem_ast->child[0];
zend_ast *key_ast = elem_ast->child[1];
zend_bool by_ref = elem_ast->attr;
znode key_node, value_node;
zval *key = &key_node.u.constant, *value = &value_node.u.constant;
if (by_ref) {
goto failure;
}
if (FAILURE == zend_compile_expr_maybe_ct(&value_node, value_ast, 1 TSRMLS_CC)) {
goto failure;
}
if (key_ast) {
if (FAILURE == zend_compile_expr_maybe_ct(&key_node, key_ast, 1 TSRMLS_CC)) {
zval_ptr_dtor(value);
goto failure;
}
}
if (Z_TYPE_P(array) == IS_UNDEF) {
array_init_size(array, ast->children);
}
if (key_ast) {
switch (Z_TYPE_P(key)) {
case IS_LONG:
zend_hash_index_update(Z_ARRVAL_P(array), Z_LVAL_P(key), value);
break;
case IS_STRING:
zend_symtable_update(Z_ARRVAL_P(array), Z_STR_P(key), value);
break;
case IS_DOUBLE:
zend_hash_index_update(Z_ARRVAL_P(array),
zend_dval_to_lval(Z_DVAL_P(key)), value);
break;
case IS_FALSE:
zend_hash_index_update(Z_ARRVAL_P(array), 0, value);
break;
case IS_TRUE:
zend_hash_index_update(Z_ARRVAL_P(array), 1, value);
break;
case IS_NULL:
zend_hash_update(Z_ARRVAL_P(array), STR_EMPTY_ALLOC(), value);
break;
default:
zend_error(E_COMPILE_ERROR, "Illegal offset type");
break;
}
zval_ptr_dtor(key);
} else {
zend_hash_next_index_insert(Z_ARRVAL_P(array), value);
}
}
if (!ast->children) {
array_init(array);
}
zend_make_immutable_array(array TSRMLS_CC);
return SUCCESS;
failure:
zval_dtor(array);
return FAILURE;
}
int zend_compile_array_maybe_ct(znode *result, zend_ast *ast, zend_bool ct_required TSRMLS_DC) {
void zend_compile_array(znode *result, zend_ast *ast TSRMLS_DC) {
zend_op *opline;
zend_uint i, opnum_init;
zend_bool packed = 1;
zval array;
if (SUCCESS == zend_try_ct_compile_array(&array, ast TSRMLS_CC)) {
result->op_type = IS_CONST;
ZVAL_COPY_VALUE(&result->u.constant, &array);
return SUCCESS;
} else if (ct_required) {
return FAILURE;
}
opnum_init = get_next_op_number(CG(active_op_array));
for (i = 0; i < ast->children; ++i) {
@ -7729,8 +7629,6 @@ int zend_compile_array_maybe_ct(znode *result, zend_ast *ast, zend_bool ct_requi
opline = &CG(active_op_array)->opcodes[opnum_init];
opline->extended_value |= ZEND_ARRAY_NOT_PACKED;
}
return FAILURE;
}
void zend_compile_const(znode *result, zend_ast *ast TSRMLS_DC) {
@ -7935,7 +7833,7 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) {
zend_compile_compound_assign(result, ast TSRMLS_CC);
return;
case ZEND_AST_BINARY_OP:
zend_compile_binary_op_maybe_ct(result, ast, 0 TSRMLS_CC);
zend_compile_binary_op(result, ast TSRMLS_CC);
return;
case ZEND_AST_GREATER:
case ZEND_AST_GREATER_EQUAL:
@ -7993,7 +7891,7 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) {
zend_compile_shell_exec(result, ast TSRMLS_CC);
return;
case ZEND_AST_ARRAY:
zend_compile_array_maybe_ct(result, ast, 0 TSRMLS_CC);
zend_compile_array(result, ast TSRMLS_CC);
return;
case ZEND_AST_CONST:
zend_compile_const(result, ast TSRMLS_CC);
@ -8012,24 +7910,6 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) {
}
}
int zend_compile_expr_maybe_ct(znode *result, zend_ast *ast, zend_bool ct_required TSRMLS_DC) {
switch (ast->kind) {
case ZEND_CONST:
ZVAL_COPY(&result->u.constant, zend_ast_get_zval(ast));
result->op_type = IS_CONST;
return SUCCESS;
case ZEND_AST_BINARY_OP:
return zend_compile_binary_op_maybe_ct(result, ast, ct_required TSRMLS_CC);
case ZEND_AST_ARRAY:
return zend_compile_array_maybe_ct(result, ast, ct_required TSRMLS_CC);
default:
if (!ct_required) {
zend_compile_expr(result, ast TSRMLS_CC);
}
return FAILURE;
}
}
void zend_compile_var(znode *result, zend_ast *ast, int type TSRMLS_DC) {
switch (ast->kind) {
case ZEND_AST_VAR:
@ -8069,6 +7949,106 @@ void zend_compile_var(znode *result, zend_ast *ast, int type TSRMLS_DC) {
}
}
void zend_eval_const_binary_op(zend_ast **ast_ptr TSRMLS_DC) {
zend_ast *ast = *ast_ptr;
zend_ast *left_ast = ast->child[0];
zend_ast *right_ast = ast->child[1];
zend_uchar opcode = ast->attr;
if (left_ast->kind == ZEND_CONST && right_ast->kind == ZEND_CONST) {
binary_op_type op = get_binary_op(opcode);
zval result;
op(&result, zend_ast_get_zval(left_ast), zend_ast_get_zval(right_ast) TSRMLS_CC);
zend_ast_destroy(ast);
*ast_ptr = zend_ast_create_constant(&result);
}
}
void zend_eval_const_array(zend_ast **ast_ptr TSRMLS_DC) {
zend_ast *ast = *ast_ptr;
zend_uint i;
zval array;
/* First ensure that *all* child nodes are constant */
for (i = 0; i < ast->children; ++i) {
zend_ast *elem_ast = ast->child[i];
zend_ast *value_ast = elem_ast->child[0];
zend_ast *key_ast = elem_ast->child[1];
zend_bool by_ref = elem_ast->attr;
if (by_ref || (key_ast && key_ast->kind != ZEND_CONST) || value_ast->kind != ZEND_CONST) {
return;
}
}
array_init_size(&array, ast->children);
for (i = 0; i < ast->children; ++i) {
zend_ast *elem_ast = ast->child[i];
zend_ast *value_ast = elem_ast->child[0];
zend_ast *key_ast = elem_ast->child[1];
zval *value = zend_ast_get_zval(value_ast);
if (Z_REFCOUNTED_P(value)) Z_ADDREF_P(value);
if (key_ast) {
zval *key = zend_ast_get_zval(key_ast);
switch (Z_TYPE_P(key)) {
case IS_LONG:
zend_hash_index_update(Z_ARRVAL(array), Z_LVAL_P(key), value);
break;
case IS_STRING:
zend_symtable_update(Z_ARRVAL(array), Z_STR_P(key), value);
break;
case IS_DOUBLE:
zend_hash_index_update(Z_ARRVAL(array),
zend_dval_to_lval(Z_DVAL_P(key)), value);
break;
case IS_FALSE:
zend_hash_index_update(Z_ARRVAL(array), 0, value);
break;
case IS_TRUE:
zend_hash_index_update(Z_ARRVAL(array), 1, value);
break;
case IS_NULL:
zend_hash_update(Z_ARRVAL(array), STR_EMPTY_ALLOC(), value);
break;
default:
zend_error(E_COMPILE_ERROR, "Illegal offset type");
break;
}
} else {
zend_hash_next_index_insert(Z_ARRVAL(array), value);
}
}
zend_ast_destroy(ast);
zend_make_immutable_array(&array TSRMLS_CC);
*ast_ptr = zend_ast_create_constant(&array);
}
void zend_eval_const_expr(zend_ast **ast_ptr TSRMLS_DC) {
zend_ast *ast = *ast_ptr;
if (!ast || ast->kind == ZEND_CONST || ast->kind == ZEND_AST_ZNODE) {
return;
}
{
zend_uint i;
for (i = 0; i < ast->children; ++i) {
zend_eval_const_expr(&ast->child[i] TSRMLS_CC);
}
}
switch (ast->kind) {
case ZEND_AST_BINARY_OP:
zend_eval_const_binary_op(ast_ptr TSRMLS_CC);
break;
case ZEND_AST_ARRAY:
zend_eval_const_array(ast_ptr TSRMLS_CC);
break;
}
}
/*
* Local variables:
* tab-width: 4

View File

@ -104,6 +104,7 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC);
void zend_compile_expr(znode *node, zend_ast *ast TSRMLS_DC);
int zend_compile_expr_maybe_ct(znode *node, zend_ast *ast, zend_bool ct_required TSRMLS_DC);
void zend_compile_var(znode *node, zend_ast *ast, int type TSRMLS_DC);
void zend_eval_const_expr(zend_ast **ast_ptr TSRMLS_DC);
typedef struct _zend_execute_data zend_execute_data;