Skip to content

Commit 12db8aa

Browse files
committed
try to fix JIT/Optimizations
1 parent 656be82 commit 12db8aa

File tree

9 files changed

+105
-33
lines changed

9 files changed

+105
-33
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -436,21 +436,14 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
436436
Tsource[VAR_NUM(opline->op1.var)] = NULL;
437437
break;
438438
}
439-
ZEND_FALLTHROUGH;
440-
441-
case ZEND_IS_EQUAL:
442-
case ZEND_IS_NOT_EQUAL:
443439
if (opline->op1_type == IS_CONST &&
444-
opline->op2_type == IS_CONST) {
440+
opline->op2_type == IS_CONST) {
445441
goto optimize_constant_binary_op;
446-
}
447-
/* IS_EQ(TRUE, X) => BOOL(X)
448-
* IS_EQ(FALSE, X) => BOOL_NOT(X)
449-
* IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
450-
* IS_NOT_EQ(FALSE, X) => BOOL(X)
451-
* CASE(TRUE, X) => BOOL(X)
452-
* CASE(FALSE, X) => BOOL_NOT(X)
453-
*/
442+
}
443+
/*
444+
* CASE(TRUE, X) => BOOL(X)
445+
* CASE(FALSE, X) => BOOL_NOT(X)
446+
*/
454447
if (opline->op1_type == IS_CONST &&
455448
(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
456449
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
@@ -464,19 +457,34 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
464457
SET_UNUSED(opline->op2);
465458
++(*opt_count);
466459
goto optimize_bool;
467-
} else if (opline->op2_type == IS_CONST &&
468-
(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
469-
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
470-
/* Optimization of comparison with "null" is not safe,
471-
* because ("0" == null) is not equal to !("0")
472-
*/
473-
opline->opcode =
474-
((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
475-
ZEND_BOOL : ZEND_BOOL_NOT;
476-
SET_UNUSED(opline->op2);
477-
++(*opt_count);
478-
goto optimize_bool;
460+
} else if (opline->op2_type == IS_CONST &&
461+
(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
462+
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
463+
/* Optimization of comparison with "null" is not safe,
464+
* because ("0" == null) is not equal to !("0")
465+
*/
466+
opline->opcode =
467+
((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
468+
ZEND_BOOL : ZEND_BOOL_NOT;
469+
SET_UNUSED(opline->op2);
470+
++(*opt_count);
471+
goto optimize_bool;
472+
}
473+
break;
474+
475+
case ZEND_IS_EQUAL:
476+
case ZEND_IS_NOT_EQUAL:
477+
if (opline->op1_type == IS_CONST &&
478+
opline->op2_type == IS_CONST) {
479+
goto optimize_constant_binary_op;
479480
}
481+
/* IS_EQ(TRUE, X) => BOOL(X)
482+
* IS_EQ(FALSE, X) => BOOL_NOT(X)
483+
* IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
484+
* IS_NOT_EQ(FALSE, X) => BOOL(X)
485+
* Those optimizations are not safe if the other operand end up being NAN
486+
* as BOOL/BOOL_NOT will warn which IS_EQUAL/IS_NOT_EQUAL do not.
487+
*/
480488
break;
481489
case ZEND_IS_IDENTICAL:
482490
if (opline->op1_type == IS_CONST &&

Zend/Optimizer/dce.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ static inline bool may_have_side_effects(
9595
case ZEND_DIV:
9696
case ZEND_MOD:
9797
case ZEND_BOOL_XOR:
98-
case ZEND_BOOL:
99-
case ZEND_BOOL_NOT:
10098
case ZEND_BW_NOT:
10199
case ZEND_SL:
102100
case ZEND_SR:
@@ -106,7 +104,6 @@ static inline bool may_have_side_effects(
106104
case ZEND_IS_SMALLER_OR_EQUAL:
107105
case ZEND_CASE:
108106
case ZEND_CASE_STRICT:
109-
case ZEND_CAST:
110107
case ZEND_ROPE_INIT:
111108
case ZEND_ROPE_ADD:
112109
case ZEND_INIT_ARRAY:
@@ -126,6 +123,27 @@ static inline bool may_have_side_effects(
126123
case ZEND_ARRAY_KEY_EXISTS:
127124
/* No side effects */
128125
return 0;
126+
case ZEND_CAST: {
127+
uint32_t t1 = OP1_INFO();
128+
/* Cast from NAN emits warning */
129+
if (t1 & MAY_BE_DOUBLE) {
130+
return true;
131+
}
132+
if (t1 & MAY_BE_ARRAY) {
133+
/* Array cast to string emits warning */
134+
return opline->extended_value == IS_STRING;
135+
}
136+
return false;
137+
}
138+
case ZEND_BOOL:
139+
case ZEND_BOOL_NOT: {
140+
uint32_t t1 = OP1_INFO();
141+
/* Cast from NAN emits warning */
142+
if (t1 & MAY_BE_DOUBLE) {
143+
return true;
144+
}
145+
return false;
146+
}
129147
case ZEND_FREE:
130148
return opline->extended_value == ZEND_FREE_VOID_CAST;
131149
case ZEND_ADD_ARRAY_ELEMENT:

Zend/Optimizer/sccp.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ static inline zend_result ct_eval_bool_cast(zval *result, zval *op) {
335335
ZVAL_TRUE(result);
336336
return SUCCESS;
337337
}
338+
/* NAN warns when casting */
339+
if (Z_TYPE_P(op) == IS_DOUBLE && zend_isnan(Z_DVAL_P(op))) {
340+
return FAILURE;
341+
}
338342

339343
ZVAL_BOOL(result, zend_is_true(op));
340344
return SUCCESS;

Zend/Optimizer/zend_inference.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5105,14 +5105,16 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
51055105
case ZEND_PRE_DEC:
51065106
case ZEND_POST_DEC:
51075107
return (t1 & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
5108-
case ZEND_BOOL_NOT:
51095108
case ZEND_JMPZ:
51105109
case ZEND_JMPNZ:
51115110
case ZEND_JMPZ_EX:
51125111
case ZEND_JMPNZ_EX:
5113-
case ZEND_BOOL:
51145112
case ZEND_JMP_SET:
51155113
return (t1 & MAY_BE_OBJECT);
5114+
case ZEND_BOOL:
5115+
case ZEND_BOOL_NOT:
5116+
/* NAN Cast to bool will warn */
5117+
return (t1 & MAY_BE_OBJECT) || (t1 & MAY_BE_DOUBLE);
51165118
case ZEND_BOOL_XOR:
51175119
return (t1 & MAY_BE_OBJECT) || (t2 & MAY_BE_OBJECT);
51185120
case ZEND_IS_EQUAL:

Zend/Optimizer/zend_optimizer.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1
7474
}
7575
return unary_op(result, op1);
7676
} else { /* ZEND_BOOL */
77+
if (Z_TYPE_P(op1) == IS_DOUBLE && zend_isnan(Z_DVAL_P(op1))) {
78+
return FAILURE;
79+
}
7780
ZVAL_BOOL(result, zend_is_true(op1));
7881
return SUCCESS;
7982
}

Zend/zend_compile.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10027,6 +10027,19 @@ ZEND_API bool zend_unary_op_produces_error(uint32_t opcode, const zval *op)
1002710027
}
1002810028
return Z_TYPE_P(op) <= IS_TRUE || !zend_is_op_long_compatible(op);
1002910029
}
10030+
/* Can happen when called from zend_optimizer_eval_unary_op() */
10031+
if (
10032+
opcode == ZEND_IS_EQUAL
10033+
|| opcode == ZEND_IS_NOT_EQUAL
10034+
|| opcode == ZEND_BOOL
10035+
|| opcode == ZEND_BOOL_NOT
10036+
) {
10037+
/* BW_NOT on string does not convert the string into an integer. */
10038+
if (Z_TYPE_P(op) == IS_DOUBLE) {
10039+
return true;
10040+
}
10041+
return false;
10042+
}
1003010043

1003110044
return 0;
1003210045
}
@@ -10210,7 +10223,7 @@ static void zend_compile_binary_op(znode *result, zend_ast *ast) /* {{{ */
1021010223
}
1021110224

1021210225
do {
10213-
// TODO do not do this for NAN?
10226+
/* TODO: Do this optimization when other side is not float as NAN will warn and we don't want that
1021410227
if (opcode == ZEND_IS_EQUAL || opcode == ZEND_IS_NOT_EQUAL) {
1021510228
if (left_node.op_type == IS_CONST) {
1021610229
if (Z_TYPE(left_node.u.constant) == IS_FALSE) {
@@ -10233,7 +10246,8 @@ static void zend_compile_binary_op(znode *result, zend_ast *ast) /* {{{ */
1023310246
break;
1023410247
}
1023510248
}
10236-
} else if (opcode == ZEND_IS_IDENTICAL || opcode == ZEND_IS_NOT_IDENTICAL) {
10249+
} else */
10250+
if (opcode == ZEND_IS_IDENTICAL || opcode == ZEND_IS_NOT_IDENTICAL) {
1023710251
/* convert $x === null to is_null($x) (i.e. ZEND_TYPE_CHECK opcode). Do the same thing for false/true. (covers IS_NULL, IS_FALSE, and IS_TRUE) */
1023810252
if (left_node.op_type == IS_CONST) {
1023910253
if (Z_TYPE(left_node.u.constant) <= IS_TRUE && Z_TYPE(left_node.u.constant) >= IS_NULL) {
@@ -12040,6 +12054,10 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
1204012054

1204112055
bool zend_try_ct_eval_cast(zval *result, uint32_t type, zval *op1)
1204212056
{
12057+
/* NAN warns when casting */
12058+
if (UNEXPECTED(Z_TYPE_P(op1) == IS_DOUBLE && zend_isnan(Z_DVAL_P(op1)))) {
12059+
return false;
12060+
}
1204312061
switch (type) {
1204412062
case _IS_BOOL:
1204512063
ZVAL_BOOL(result, zval_is_true(op1));

Zend/zend_execute.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ static zend_always_inline void zend_cast_zval_to_object(zval *result, zval *expr
233233
}
234234
Z_OBJ_P(result)->properties = ht;
235235
} else if (Z_TYPE_P(expr) != IS_NULL) {
236+
if (UNEXPECTED(Z_TYPE_P(expr) == IS_DOUBLE && zend_isnan(Z_DVAL_P(expr)))) {
237+
zend_nan_coerced_to_type_warning(IS_OBJECT);
238+
}
236239
Z_OBJ_P(result)->properties = ht = zend_new_array(1);
237240
expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
238241
if (op1_type == IS_CONST) {
@@ -247,6 +250,9 @@ static zend_always_inline void zend_cast_zval_to_array(zval *result, zval *expr,
247250
extern zend_class_entry *zend_ce_closure;
248251
if (op1_type == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
249252
if (Z_TYPE_P(expr) != IS_NULL) {
253+
if (UNEXPECTED(Z_TYPE_P(expr) == IS_DOUBLE && zend_isnan(Z_DVAL_P(expr)))) {
254+
zend_nan_coerced_to_type_warning(IS_ARRAY);
255+
}
250256
ZVAL_ARR(result, zend_new_array(1));
251257
expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
252258
if (op1_type == IS_CONST) {

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
+----------------------------------------------------------------------+
1717
*/
1818

19+
#include "Zend/zend_portability.h"
1920
#include "Zend/zend_types.h"
2021
#include "Zend/zend_API.h"
2122

@@ -2575,6 +2576,11 @@ static void ZEND_FASTCALL zend_jit_invalid_array_access(zval *container)
25752576
zend_error(E_WARNING, "Trying to access array offset on %s", zend_zval_value_name(container));
25762577
}
25772578

2579+
static void ZEND_FASTCALL zend_jit_check_nan_to_bool_coercion(void)
2580+
{
2581+
zend_nan_coerced_to_type_warning(_IS_BOOL);
2582+
}
2583+
25782584
static void ZEND_FASTCALL zend_jit_invalid_property_read(zval *container, const char *property_name)
25792585
{
25802586
zend_error(E_WARNING, "Attempt to read property \"%s\" on %s", property_name, zend_zval_value_name(container));

ext/opcache/jit/zend_jit_ir.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7729,7 +7729,14 @@ static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32
77297729
if_double = ir_IF(ir_EQ(type, ir_CONST_U8(IS_DOUBLE)));
77307730
ir_IF_TRUE(if_double);
77317731
}
7732-
ref = ir_NE(jit_Z_DVAL(jit, op1_addr), ir_CONST_DOUBLE(0.0));
7732+
7733+
ir_ref dval = jit_Z_DVAL(jit, op1_addr);ir_ref is_nan = ir_NE(dval, dval);
7734+
ir_ref if_val = ir_IF(is_nan);
7735+
ir_IF_TRUE_cold(if_val);
7736+
ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_jit_check_nan_to_bool_coercion));
7737+
ir_MERGE_WITH_EMPTY_FALSE(if_val);
7738+
7739+
ref = ir_NE(dval, ir_CONST_DOUBLE(0.0));
77337740
if (branch_opcode == ZEND_BOOL || branch_opcode == ZEND_BOOL_NOT) {
77347741
if (set_bool_not) {
77357742
jit_set_Z_TYPE_INFO_ref(jit, jit_ZVAL_ADDR(jit, res_addr),

0 commit comments

Comments
 (0)