Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,53 @@ static inline bool can_elide_return_type_check(
return false;
}

static inline bool can_return_value_safely_be_coerced(
const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op, zend_op *opline
) {
const uint32_t return_type_mask = ZEND_TYPE_FULL_MASK(op_array->arg_info[-1].type);
const zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];

/* Type preference order: int -> float -> string -> bool */
/* Can always safely cast booleans to inter */
if (return_type_mask & MAY_BE_LONG) {
if (use_info->type & MAY_BE_BOOL) {
opline->opcode = ZEND_CAST;
opline->extended_value = IS_LONG;
return true;
}
return false;
}
if (return_type_mask & MAY_BE_DOUBLE) {
/* Can always safely cast booleans, and integers to float */
if (use_info->type & (MAY_BE_LONG|MAY_BE_BOOL)) {
opline->opcode = ZEND_CAST;
opline->extended_value = IS_DOUBLE;
return true;
}
return false;
}
/* Can always safely cast booleans, and integers to string,
* float value must not be NAN */
if (return_type_mask & MAY_BE_STRING) {
if (use_info->type & (MAY_BE_LONG|MAY_BE_BOOL)) {
opline->opcode = ZEND_CAST;
opline->extended_value = IS_STRING;
return true;
}
return false;
}
return false;
if ((return_type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
/* Can always safely cast integers and strings to bool,
* float value must not be NAN */
if (use_info->type & (MAY_BE_LONG|MAY_BE_STRING)) {
opline->opcode = ZEND_BOOL;
return true;
}
return false;
}
}

static bool opline_supports_assign_contraction(
zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
if (opline->opcode == ZEND_NEW) {
Expand Down Expand Up @@ -1289,6 +1336,8 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx

MAKE_NOP(opline);
remove_nops = 1;
} else if (can_return_value_safely_be_coerced(op_array, ssa, &ssa->ops[op_1], opline)) {
continue;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/tests/opt/type_inference_class_consts1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Test1::getStaticFoo:
; (after optimizer)
; %s
0000 T0 = FETCH_CLASS_CONSTANT (static) (exception) string("FOO")
0001 VERIFY_RETURN_TYPE T0
0001 CAST (long) T0
0002 RETURN T0
LIVE RANGES:
0: 0001 - 0002 (tmp/var)
Expand Down
8 changes: 4 additions & 4 deletions ext/opcache/tests/opt/type_inference_class_consts4.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Test4::getSelfA:
; (after optimizer)
; %s
0000 T0 = FETCH_CLASS_CONSTANT (self) (exception) string("A")
0001 VERIFY_RETURN_TYPE T0
0001 CAST (long) T0
0002 RETURN T0
LIVE RANGES:
0: 0001 - 0002 (tmp/var)
Expand All @@ -67,7 +67,7 @@ Test4::getSelfB:
; (after optimizer)
; %s
0000 T0 = FETCH_CLASS_CONSTANT (self) (exception) string("B")
0001 VERIFY_RETURN_TYPE T0
0001 CAST (long) T0
0002 RETURN T0
LIVE RANGES:
0: 0001 - 0002 (tmp/var)
Expand All @@ -77,7 +77,7 @@ Test4::getStaticA:
; (after optimizer)
; %s
0000 T0 = FETCH_CLASS_CONSTANT (static) (exception) string("A")
0001 VERIFY_RETURN_TYPE T0
0001 CAST (long) T0
0002 RETURN T0
LIVE RANGES:
0: 0001 - 0002 (tmp/var)
Expand All @@ -87,7 +87,7 @@ Test4::getStaticB:
; (after optimizer)
; %s
0000 T0 = FETCH_CLASS_CONSTANT (static) (exception) string("B")
0001 VERIFY_RETURN_TYPE T0
0001 CAST (long) T0
0002 RETURN T0
LIVE RANGES:
0: 0001 - 0002 (tmp/var)
Expand Down
202 changes: 202 additions & 0 deletions ext/opcache/tests/opt/verify_return_type_to_cast.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
--TEST--
Return type check converted to a cast
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.opt_debug_level=0x20000
opcache.preload=
--EXTENSIONS--
opcache
--FILE--
<?php

function strToBool(string $v): bool {
return $v;
}
function intToBool(int $v): bool {
return $v;
}
function floatToBool(float $v): bool {
return $v;
}

function strToInt(string $v): int {
return $v;
}
function floatToInt(float $v): int {
return $v;
}
function boolToInt(bool $v): int {
return $v;
}

function strToFloat(string $v): float {
return $v;
}
function intToFloat(int $v): float {
return $v;
}
function boolToFloat(bool $v): float {
return $v;
}

function floatToString(float $v): string {
return $v;
}
function intToString(int $v): string {
return $v;
}
function boolToString(bool $v): string {
return $v;
}

function floatToUnion(float $v): string|int|bool {
return $v;
}
function intToUnion(int $v): string|float|bool {
return $v;
}
function boolToUnion(bool $v): string|int|float {
return $v;
}
function stringToUnion(string $v): bool|int|float {
return $v;
}


?>
--EXPECTF--
$_main:
; (lines=1, args=0, vars=0, tmps=0)
; (after optimizer)
; %s:1-58
0000 RETURN int(1)

strToBool:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:3-5
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

intToBool:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:6-8
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

floatToBool:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:9-11
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

strToInt:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:13-15
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

floatToInt:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:16-18
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

boolToInt:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:19-21
0000 CV0($v) = RECV 1
0001 CAST (long) CV0($v)
0002 RETURN CV0($v)

strToFloat:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:23-25
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

intToFloat:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:26-28
0000 CV0($v) = RECV 1
0001 CAST (double) CV0($v)
0002 RETURN CV0($v)

boolToFloat:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:29-31
0000 CV0($v) = RECV 1
0001 CAST (double) CV0($v)
0002 RETURN CV0($v)

floatToString:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:33-35
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

intToString:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:36-38
0000 CV0($v) = RECV 1
0001 CAST (string) CV0($v)
0002 RETURN CV0($v)

boolToString:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:39-41
0000 CV0($v) = RECV 1
0001 CAST (string) CV0($v)
0002 RETURN CV0($v)

floatToUnion:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:43-45
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)

intToUnion:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:46-48
0000 CV0($v) = RECV 1
0001 CAST (double) CV0($v)
0002 RETURN CV0($v)

boolToUnion:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:49-51
0000 CV0($v) = RECV 1
0001 CAST (long) CV0($v)
0002 RETURN CV0($v)

stringToUnion:
; (lines=3, args=1, vars=1, tmps=0)
; (after optimizer)
; %s:52-54
0000 CV0($v) = RECV 1
0001 VERIFY_RETURN_TYPE CV0($v)
0002 RETURN CV0($v)
Loading