1999-04-08 02:10:10 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Zend Engine |
|
|
|
|
+----------------------------------------------------------------------+
|
2019-01-30 17:23:29 +08:00
|
|
|
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
|
1999-04-08 02:10:10 +08:00
|
|
|
+----------------------------------------------------------------------+
|
2001-12-11 23:16:21 +08:00
|
|
|
| This source file is subject to version 2.00 of the Zend license, |
|
2009-05-11 06:39:33 +08:00
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
2003-06-11 04:04:29 +08:00
|
|
|
| available through the world-wide-web at the following url: |
|
2001-12-11 23:16:21 +08:00
|
|
|
| http://www.zend.com/license/2_00.txt. |
|
1999-07-16 22:58:16 +08:00
|
|
|
| If you did not receive a copy of the Zend license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@zend.com so we can mail you a copy immediately. |
|
1999-04-08 02:10:10 +08:00
|
|
|
+----------------------------------------------------------------------+
|
2018-11-01 23:20:07 +08:00
|
|
|
| Authors: Andi Gutmans <andi@php.net> |
|
|
|
|
| Zeev Suraski <zeev@php.net> |
|
|
|
|
| Dmitry Stogov <dmitry@php.net> |
|
1999-04-08 02:10:10 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
2000-07-03 08:55:36 +08:00
|
|
|
#ifndef ZEND_OPERATORS_H
|
|
|
|
#define ZEND_OPERATORS_H
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2000-06-14 01:58:33 +08:00
|
|
|
#include <errno.h>
|
2000-06-15 03:55:32 +08:00
|
|
|
#include <math.h>
|
2006-05-10 07:53:23 +08:00
|
|
|
#include <assert.h>
|
2013-05-08 02:37:04 +08:00
|
|
|
#include <stddef.h>
|
|
|
|
|
2000-06-19 00:33:15 +08:00
|
|
|
#ifdef HAVE_IEEEFP_H
|
2000-06-15 03:55:32 +08:00
|
|
|
#include <ieeefp.h>
|
|
|
|
#endif
|
|
|
|
|
2016-08-10 07:15:13 +08:00
|
|
|
#include "zend_portability.h"
|
2004-11-04 07:13:32 +08:00
|
|
|
#include "zend_strtod.h"
|
2011-05-23 16:05:44 +08:00
|
|
|
#include "zend_multiply.h"
|
2017-11-20 17:38:31 +08:00
|
|
|
#include "zend_object_handlers.h"
|
2000-11-22 06:41:49 +08:00
|
|
|
|
2019-12-31 06:13:39 +08:00
|
|
|
#define LONG_SIGN_MASK ZEND_LONG_MIN
|
2011-05-23 16:05:44 +08:00
|
|
|
|
2004-02-20 16:03:27 +08:00
|
|
|
BEGIN_EXTERN_C()
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL mod_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL boolean_xor_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL boolean_not_function(zval *result, zval *op1);
|
|
|
|
ZEND_API int ZEND_FASTCALL bitwise_not_function(zval *result, zval *op1);
|
|
|
|
ZEND_API int ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL shift_left_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2);
|
|
|
|
|
2019-04-04 23:18:12 +08:00
|
|
|
ZEND_API zend_bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2);
|
2015-04-29 21:43:23 +08:00
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL is_identical_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL is_not_identical_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL is_smaller_function(zval *result, zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zval *op1, zval *op2);
|
|
|
|
|
2019-10-25 16:45:42 +08:00
|
|
|
ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce);
|
|
|
|
ZEND_API zend_bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce);
|
|
|
|
|
|
|
|
static zend_always_inline zend_bool instanceof_function(
|
|
|
|
const zend_class_entry *instance_ce, const zend_class_entry *ce) {
|
|
|
|
return instance_ce == ce || instanceof_function_slow(instance_ce, ce);
|
|
|
|
}
|
2014-10-03 19:07:02 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the string "str" with length "length" is numeric. The value
|
|
|
|
* of allow_errors determines whether it's required to be entirely numeric, or
|
|
|
|
* just its prefix. Leading whitespace is allowed.
|
|
|
|
*
|
|
|
|
* The function returns 0 if the string did not contain a valid number; IS_LONG
|
|
|
|
* if it contained a number that fits within the range of a long; or IS_DOUBLE
|
|
|
|
* if the number was out of long range or contained a decimal point/exponent.
|
|
|
|
* The number's value is returned into the respective pointer, *lval or *dval,
|
|
|
|
* if that pointer is not NULL.
|
|
|
|
*
|
|
|
|
* This variant also gives information if a string that represents an integer
|
|
|
|
* could not be represented as such due to overflow. It writes 1 to oflow_info
|
|
|
|
* if the integer is larger than ZEND_LONG_MAX and -1 if it's smaller than ZEND_LONG_MIN.
|
|
|
|
*/
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors, int *oflow_info);
|
2014-10-03 19:07:02 +08:00
|
|
|
|
2015-07-17 23:19:06 +08:00
|
|
|
ZEND_API const char* ZEND_FASTCALL zend_memnstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end);
|
|
|
|
ZEND_API const char* ZEND_FASTCALL zend_memnrstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end);
|
2015-01-12 15:34:46 +08:00
|
|
|
|
2014-11-29 10:13:20 +08:00
|
|
|
#if SIZEOF_ZEND_LONG == 4
|
2019-09-16 21:00:20 +08:00
|
|
|
# define ZEND_DOUBLE_FITS_LONG(d) (!((d) > (double)ZEND_LONG_MAX || (d) < (double)ZEND_LONG_MIN))
|
2014-11-29 10:13:20 +08:00
|
|
|
#else
|
|
|
|
/* >= as (double)ZEND_LONG_MAX is outside signed range */
|
2019-09-16 21:00:20 +08:00
|
|
|
# define ZEND_DOUBLE_FITS_LONG(d) (!((d) >= (double)ZEND_LONG_MAX || (d) < (double)ZEND_LONG_MIN))
|
2014-11-29 10:13:20 +08:00
|
|
|
#endif
|
|
|
|
|
2020-05-12 23:52:53 +08:00
|
|
|
#ifdef ZEND_DVAL_TO_LVAL_CAST_OK
|
2014-09-16 20:45:06 +08:00
|
|
|
static zend_always_inline zend_long zend_dval_to_lval(double d)
|
2014-08-18 07:39:07 +08:00
|
|
|
{
|
2014-08-20 05:03:16 +08:00
|
|
|
if (EXPECTED(zend_finite(d)) && EXPECTED(!zend_isnan(d))) {
|
2014-09-16 20:45:06 +08:00
|
|
|
return (zend_long)d;
|
2014-08-18 07:39:07 +08:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2009-01-06 04:31:54 +08:00
|
|
|
#else
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API zend_long ZEND_FASTCALL zend_dval_to_lval_slow(double d);
|
2015-02-26 06:28:47 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
static zend_always_inline zend_long zend_dval_to_lval(double d)
|
2009-06-05 02:20:45 +08:00
|
|
|
{
|
2014-09-18 03:41:20 +08:00
|
|
|
if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
|
|
|
|
return 0;
|
2014-11-29 10:13:20 +08:00
|
|
|
} else if (!ZEND_DOUBLE_FITS_LONG(d)) {
|
2015-02-26 06:28:47 +08:00
|
|
|
return zend_dval_to_lval_slow(d);
|
2009-05-11 06:39:33 +08:00
|
|
|
}
|
2014-08-26 01:24:55 +08:00
|
|
|
return (zend_long)d;
|
2009-06-05 02:20:45 +08:00
|
|
|
}
|
2009-01-06 04:31:54 +08:00
|
|
|
#endif
|
2016-03-30 08:44:27 +08:00
|
|
|
|
|
|
|
static zend_always_inline zend_long zend_dval_to_lval_cap(double d)
|
|
|
|
{
|
|
|
|
if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
|
|
|
|
return 0;
|
|
|
|
} else if (!ZEND_DOUBLE_FITS_LONG(d)) {
|
|
|
|
return (d > 0 ? ZEND_LONG_MAX : ZEND_LONG_MIN);
|
|
|
|
}
|
|
|
|
return (zend_long)d;
|
|
|
|
}
|
2009-05-11 06:39:33 +08:00
|
|
|
/* }}} */
|
2009-01-06 04:31:54 +08:00
|
|
|
|
2006-12-27 00:44:20 +08:00
|
|
|
#define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
|
2009-05-11 06:39:33 +08:00
|
|
|
#define ZEND_IS_XDIGIT(c) (((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
|
2006-12-27 00:44:20 +08:00
|
|
|
|
2014-09-19 02:19:51 +08:00
|
|
|
static zend_always_inline zend_uchar is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors, int *oflow_info)
|
|
|
|
{
|
|
|
|
if (*str > '9') {
|
2005-11-17 08:19:23 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2014-09-19 02:19:51 +08:00
|
|
|
return _is_numeric_string_ex(str, length, lval, dval, allow_errors, oflow_info);
|
2000-06-14 01:58:33 +08:00
|
|
|
}
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2014-09-19 02:19:51 +08:00
|
|
|
static zend_always_inline zend_uchar is_numeric_string(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors) {
|
2012-05-11 14:58:10 +08:00
|
|
|
return is_numeric_string_ex(str, length, lval, dval, allow_errors, NULL);
|
|
|
|
}
|
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API zend_uchar ZEND_FASTCALL is_numeric_str_function(const zend_string *str, zend_long *lval, double *dval);
|
2014-07-11 20:32:20 +08:00
|
|
|
|
2014-09-19 02:19:51 +08:00
|
|
|
static zend_always_inline const char *
|
2015-07-17 23:19:06 +08:00
|
|
|
zend_memnstr(const char *haystack, const char *needle, size_t needle_len, const char *end)
|
2003-04-08 23:04:26 +08:00
|
|
|
{
|
2013-07-30 18:49:36 +08:00
|
|
|
const char *p = haystack;
|
2014-08-16 17:16:11 +08:00
|
|
|
ptrdiff_t off_p;
|
2014-08-26 01:24:55 +08:00
|
|
|
size_t off_s;
|
2003-04-08 23:04:26 +08:00
|
|
|
|
2019-08-18 21:33:10 +08:00
|
|
|
if (needle_len == 0) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2008-07-22 02:47:02 +08:00
|
|
|
if (needle_len == 1) {
|
2015-01-12 17:24:37 +08:00
|
|
|
return (const char *)memchr(p, *needle, (end-p));
|
2008-07-22 02:47:02 +08:00
|
|
|
}
|
|
|
|
|
2014-08-16 17:16:11 +08:00
|
|
|
off_p = end - haystack;
|
2014-08-26 01:24:55 +08:00
|
|
|
off_s = (off_p > 0) ? (size_t)off_p : 0;
|
2015-01-12 17:24:37 +08:00
|
|
|
|
2014-08-16 17:16:11 +08:00
|
|
|
if (needle_len > off_s) {
|
2008-08-06 04:14:27 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-18 14:46:40 +08:00
|
|
|
if (EXPECTED(off_s < 1024 || needle_len < 9)) { /* glibc memchr is faster when needle is too short */
|
2019-08-28 15:32:18 +08:00
|
|
|
const char ne = needle[needle_len-1];
|
2015-01-12 15:34:46 +08:00
|
|
|
end -= needle_len;
|
2003-04-08 23:04:26 +08:00
|
|
|
|
2015-01-12 15:34:46 +08:00
|
|
|
while (p <= end) {
|
2015-01-12 17:24:37 +08:00
|
|
|
if ((p = (const char *)memchr(p, *needle, (end-p+1))) && ne == p[needle_len-1]) {
|
2017-10-12 16:54:40 +08:00
|
|
|
if (!memcmp(needle+1, p+1, needle_len-2)) {
|
2015-01-12 15:34:46 +08:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == NULL) {
|
|
|
|
return NULL;
|
2003-04-08 23:04:26 +08:00
|
|
|
}
|
2006-05-10 07:53:23 +08:00
|
|
|
|
2015-01-12 15:34:46 +08:00
|
|
|
p++;
|
2003-04-08 23:04:26 +08:00
|
|
|
}
|
2006-05-10 07:53:23 +08:00
|
|
|
|
2015-01-12 15:34:46 +08:00
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
return zend_memnstr_ex(haystack, needle, needle_len, end);
|
2003-04-08 23:04:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-19 02:19:51 +08:00
|
|
|
static zend_always_inline const void *zend_memrchr(const void *s, int c, size_t n)
|
2006-08-08 17:41:09 +08:00
|
|
|
{
|
2016-02-04 20:31:21 +08:00
|
|
|
const unsigned char *e;
|
2017-11-18 01:19:32 +08:00
|
|
|
if (0 == n) {
|
2006-12-21 03:08:48 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-10-15 05:33:10 +08:00
|
|
|
for (e = (const unsigned char *)s + n - 1; e >= (const unsigned char *)s; e--) {
|
|
|
|
if (*e == (const unsigned char)c) {
|
|
|
|
return (const void *)e;
|
2006-08-08 17:41:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-02-20 16:03:27 +08:00
|
|
|
|
2015-01-12 17:24:37 +08:00
|
|
|
|
|
|
|
static zend_always_inline const char *
|
2018-03-17 03:36:41 +08:00
|
|
|
zend_memnrstr(const char *haystack, const char *needle, size_t needle_len, const char *end)
|
2015-01-12 17:24:37 +08:00
|
|
|
{
|
|
|
|
const char *p = end;
|
|
|
|
ptrdiff_t off_p;
|
|
|
|
size_t off_s;
|
|
|
|
|
2019-08-18 21:33:10 +08:00
|
|
|
if (needle_len == 0) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2015-01-12 17:24:37 +08:00
|
|
|
if (needle_len == 1) {
|
|
|
|
return (const char *)zend_memrchr(haystack, *needle, (p - haystack));
|
|
|
|
}
|
|
|
|
|
|
|
|
off_p = end - haystack;
|
|
|
|
off_s = (off_p > 0) ? (size_t)off_p : 0;
|
|
|
|
|
|
|
|
if (needle_len > off_s) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EXPECTED(off_s < 1024 || needle_len < 3)) {
|
2019-08-28 15:32:18 +08:00
|
|
|
const char ne = needle[needle_len-1];
|
2015-01-12 17:24:37 +08:00
|
|
|
p -= needle_len;
|
|
|
|
|
|
|
|
do {
|
2019-06-20 18:18:54 +08:00
|
|
|
p = (const char *)zend_memrchr(haystack, *needle, (p - haystack) + 1);
|
|
|
|
if (!p) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ne == p[needle_len-1] && !memcmp(needle + 1, p + 1, needle_len - 2)) {
|
|
|
|
return p;
|
2015-01-12 17:24:37 +08:00
|
|
|
}
|
|
|
|
} while (p-- >= haystack);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
return zend_memnrstr_ex(haystack, needle, needle_len, end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL increment_function(zval *op1);
|
|
|
|
ZEND_API int ZEND_FASTCALL decrement_function(zval *op2);
|
|
|
|
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op);
|
2018-07-23 20:24:07 +08:00
|
|
|
ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op);
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_long(zval *op);
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_double(zval *op);
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_long_base(zval *op, int base);
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_null(zval *op);
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_boolean(zval *op);
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_array(zval *op);
|
|
|
|
ZEND_API void ZEND_FASTCALL convert_to_object(zval *op);
|
2014-04-16 00:33:38 +08:00
|
|
|
|
2017-11-16 22:09:01 +08:00
|
|
|
ZEND_API zend_long ZEND_FASTCALL zval_get_long_func(zval *op);
|
|
|
|
ZEND_API double ZEND_FASTCALL zval_get_double_func(zval *op);
|
|
|
|
ZEND_API zend_string* ZEND_FASTCALL zval_get_string_func(zval *op);
|
2019-02-26 22:32:18 +08:00
|
|
|
ZEND_API zend_string* ZEND_FASTCALL zval_try_get_string_func(zval *op);
|
2014-04-22 02:36:01 +08:00
|
|
|
|
2017-11-16 22:09:01 +08:00
|
|
|
static zend_always_inline zend_long zval_get_long(zval *op) {
|
|
|
|
return EXPECTED(Z_TYPE_P(op) == IS_LONG) ? Z_LVAL_P(op) : zval_get_long_func(op);
|
2014-04-25 20:41:12 +08:00
|
|
|
}
|
2017-11-16 22:09:01 +08:00
|
|
|
static zend_always_inline double zval_get_double(zval *op) {
|
|
|
|
return EXPECTED(Z_TYPE_P(op) == IS_DOUBLE) ? Z_DVAL_P(op) : zval_get_double_func(op);
|
2014-04-25 20:41:12 +08:00
|
|
|
}
|
2017-11-16 22:09:01 +08:00
|
|
|
static zend_always_inline zend_string *zval_get_string(zval *op) {
|
|
|
|
return EXPECTED(Z_TYPE_P(op) == IS_STRING) ? zend_string_copy(Z_STR_P(op)) : zval_get_string_func(op);
|
2014-04-25 20:41:12 +08:00
|
|
|
}
|
2014-04-25 19:37:07 +08:00
|
|
|
|
2017-11-16 22:09:32 +08:00
|
|
|
static zend_always_inline zend_string *zval_get_tmp_string(zval *op, zend_string **tmp) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) {
|
|
|
|
*tmp = NULL;
|
|
|
|
return Z_STR_P(op);
|
|
|
|
} else {
|
|
|
|
return *tmp = zval_get_string_func(op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static zend_always_inline void zend_tmp_string_release(zend_string *tmp) {
|
|
|
|
if (UNEXPECTED(tmp)) {
|
2018-05-28 21:27:12 +08:00
|
|
|
zend_string_release_ex(tmp, 0);
|
2017-11-16 22:09:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-26 22:32:18 +08:00
|
|
|
/* Like zval_get_string, but returns NULL if the conversion fails with an exception. */
|
|
|
|
static zend_always_inline zend_string *zval_try_get_string(zval *op) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) {
|
2019-06-06 19:22:10 +08:00
|
|
|
zend_string *ret = zend_string_copy(Z_STR_P(op));
|
|
|
|
ZEND_ASSUME(ret != NULL);
|
|
|
|
return ret;
|
2019-02-26 22:32:18 +08:00
|
|
|
} else {
|
|
|
|
return zval_try_get_string_func(op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Like zval_get_tmp_string, but returns NULL if the conversion fails with an exception. */
|
|
|
|
static zend_always_inline zend_string *zval_try_get_tmp_string(zval *op, zend_string **tmp) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) {
|
2019-06-06 19:22:10 +08:00
|
|
|
zend_string *ret = Z_STR_P(op);
|
2019-02-26 22:32:18 +08:00
|
|
|
*tmp = NULL;
|
2019-06-06 19:22:10 +08:00
|
|
|
ZEND_ASSUME(ret != NULL);
|
|
|
|
return ret;
|
2019-02-26 22:32:18 +08:00
|
|
|
} else {
|
|
|
|
return *tmp = zval_try_get_string_func(op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Like convert_to_string(), but returns whether the conversion succeeded and does not modify the
|
|
|
|
* zval in-place if it fails. */
|
|
|
|
ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op);
|
|
|
|
static zend_always_inline zend_bool try_convert_to_string(zval *op) {
|
|
|
|
if (Z_TYPE_P(op) == IS_STRING) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return _try_convert_to_string(op);
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:09:01 +08:00
|
|
|
/* Compatibility macros for 7.2 and below */
|
|
|
|
#define _zval_get_long(op) zval_get_long(op)
|
|
|
|
#define _zval_get_double(op) zval_get_double(op)
|
|
|
|
#define _zval_get_string(op) zval_get_string(op)
|
|
|
|
#define _zval_get_long_func(op) zval_get_long_func(op)
|
|
|
|
#define _zval_get_double_func(op) zval_get_double_func(op)
|
|
|
|
#define _zval_get_string_func(op) zval_get_string_func(op)
|
2014-04-16 00:33:38 +08:00
|
|
|
|
2018-07-23 20:24:07 +08:00
|
|
|
#define convert_to_string(op) if (Z_TYPE_P(op) != IS_STRING) { _convert_to_string((op)); }
|
2000-05-26 15:49:56 +08:00
|
|
|
|
2014-12-11 18:18:40 +08:00
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zend_is_true(zval *op);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_object_is_true(zval *op);
|
2014-12-11 18:18:40 +08:00
|
|
|
|
|
|
|
#define zval_is_true(op) \
|
2014-12-14 06:06:14 +08:00
|
|
|
zend_is_true(op)
|
2014-12-11 18:18:40 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static zend_always_inline int i_zend_is_true(zval *op)
|
2014-12-11 18:18:40 +08:00
|
|
|
{
|
2015-09-10 16:13:22 +08:00
|
|
|
int result = 0;
|
2014-12-11 18:18:40 +08:00
|
|
|
|
|
|
|
again:
|
|
|
|
switch (Z_TYPE_P(op)) {
|
|
|
|
case IS_TRUE:
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
case IS_LONG:
|
2015-09-10 16:13:22 +08:00
|
|
|
if (Z_LVAL_P(op)) {
|
|
|
|
result = 1;
|
|
|
|
}
|
2014-12-11 18:18:40 +08:00
|
|
|
break;
|
|
|
|
case IS_DOUBLE:
|
2015-09-10 16:13:22 +08:00
|
|
|
if (Z_DVAL_P(op)) {
|
|
|
|
result = 1;
|
|
|
|
}
|
2014-12-11 18:18:40 +08:00
|
|
|
break;
|
|
|
|
case IS_STRING:
|
2015-09-10 16:13:22 +08:00
|
|
|
if (Z_STRLEN_P(op) > 1 || (Z_STRLEN_P(op) && Z_STRVAL_P(op)[0] != '0')) {
|
2014-12-11 18:18:40 +08:00
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IS_ARRAY:
|
2015-09-10 16:13:22 +08:00
|
|
|
if (zend_hash_num_elements(Z_ARRVAL_P(op))) {
|
|
|
|
result = 1;
|
|
|
|
}
|
2014-12-11 18:18:40 +08:00
|
|
|
break;
|
|
|
|
case IS_OBJECT:
|
2017-11-20 17:38:31 +08:00
|
|
|
if (EXPECTED(Z_OBJ_HT_P(op)->cast_object == zend_std_cast_object_tostring)) {
|
|
|
|
result = 1;
|
|
|
|
} else {
|
|
|
|
result = zend_object_is_true(op);
|
|
|
|
}
|
2014-12-11 18:18:40 +08:00
|
|
|
break;
|
2015-09-10 16:13:22 +08:00
|
|
|
case IS_RESOURCE:
|
|
|
|
if (EXPECTED(Z_RES_HANDLE_P(op))) {
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
break;
|
2014-12-11 18:18:40 +08:00
|
|
|
case IS_REFERENCE:
|
|
|
|
op = Z_REFVAL_P(op);
|
|
|
|
goto again;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-03-31 18:36:48 +08:00
|
|
|
/* Indicate that two values cannot be compared. This value should be returned for both orderings
|
|
|
|
* of the operands. This implies that all of ==, <, <= and >, >= will return false, because we
|
|
|
|
* canonicalize >/>= to </<= with swapped operands. */
|
|
|
|
// TODO: Use a different value to allow an actual distinction here.
|
|
|
|
#define ZEND_UNCOMPARABLE 1
|
|
|
|
|
2019-10-07 22:57:49 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2);
|
|
|
|
|
2015-06-22 17:53:52 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2);
|
2015-09-10 07:51:23 +08:00
|
|
|
|
|
|
|
ZEND_API int ZEND_FASTCALL numeric_compare_function(zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, zend_bool case_insensitive);
|
|
|
|
ZEND_API int ZEND_FASTCALL string_compare_function(zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL string_case_compare_function(zval *op1, zval *op2);
|
|
|
|
ZEND_API int ZEND_FASTCALL string_locale_compare_function(zval *op1, zval *op2);
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API void ZEND_FASTCALL zend_str_tolower(char *str, size_t length);
|
|
|
|
ZEND_API char* ZEND_FASTCALL zend_str_tolower_copy(char *dest, const char *source, size_t length);
|
|
|
|
ZEND_API char* ZEND_FASTCALL zend_str_tolower_dup(const char *source, size_t length);
|
2015-07-02 00:04:18 +08:00
|
|
|
ZEND_API char* ZEND_FASTCALL zend_str_tolower_dup_ex(const char *source, size_t length);
|
2017-11-30 06:13:39 +08:00
|
|
|
ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, int persistent);
|
|
|
|
|
|
|
|
#define zend_string_tolower(str) zend_string_tolower_ex(str, 0)
|
2003-05-22 05:59:40 +08:00
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_zval_strcmp(zval *s1, zval *s2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_zval_strncmp(zval *s1, zval *s2, zval *s3);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_zval_strcasecmp(zval *s1, zval *s2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_zval_strncasecmp(zval *s1, zval *s2, zval *s3);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_strcmp(const char *s1, size_t len1, const char *s2, size_t len2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_strncmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp(const char *s1, size_t len1, const char *s2, size_t len2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp_l(const char *s1, size_t len1, const char *s2, size_t len2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp_l(const char *s1, size_t len1, const char *s2, size_t len2, size_t length);
|
2000-01-25 03:00:30 +08:00
|
|
|
|
2018-01-16 22:24:00 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zendi_smart_streq(zend_string *s1, zend_string *s2);
|
2017-10-23 01:58:42 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zendi_smart_strcmp(zend_string *s1, zend_string *s2);
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zend_compare_symbol_tables(HashTable *ht1, HashTable *ht2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_compare_arrays(zval *a1, zval *a2);
|
|
|
|
ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2);
|
2000-06-03 18:34:19 +08:00
|
|
|
|
2017-07-28 20:54:01 +08:00
|
|
|
ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len);
|
|
|
|
ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len);
|
1999-04-08 02:10:10 +08:00
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC);
|
2009-05-11 06:39:33 +08:00
|
|
|
|
2014-02-10 14:04:30 +08:00
|
|
|
#define convert_to_ex_master(pzv, lower_type, upper_type) \
|
2014-04-30 22:32:42 +08:00
|
|
|
if (Z_TYPE_P(pzv)!=upper_type) { \
|
2014-02-10 14:04:30 +08:00
|
|
|
convert_to_##lower_type(pzv); \
|
1999-09-17 07:15:34 +08:00
|
|
|
}
|
|
|
|
|
2006-05-10 07:53:23 +08:00
|
|
|
#define convert_to_explicit_type(pzv, type) \
|
2009-05-11 06:39:33 +08:00
|
|
|
do { \
|
2006-05-10 07:53:23 +08:00
|
|
|
switch (type) { \
|
|
|
|
case IS_NULL: \
|
|
|
|
convert_to_null(pzv); \
|
|
|
|
break; \
|
|
|
|
case IS_LONG: \
|
|
|
|
convert_to_long(pzv); \
|
|
|
|
break; \
|
2009-05-11 06:39:33 +08:00
|
|
|
case IS_DOUBLE: \
|
|
|
|
convert_to_double(pzv); \
|
|
|
|
break; \
|
2014-04-30 22:32:42 +08:00
|
|
|
case _IS_BOOL: \
|
2009-05-11 06:39:33 +08:00
|
|
|
convert_to_boolean(pzv); \
|
|
|
|
break; \
|
|
|
|
case IS_ARRAY: \
|
|
|
|
convert_to_array(pzv); \
|
|
|
|
break; \
|
|
|
|
case IS_OBJECT: \
|
|
|
|
convert_to_object(pzv); \
|
|
|
|
break; \
|
|
|
|
case IS_STRING: \
|
|
|
|
convert_to_string(pzv); \
|
|
|
|
break; \
|
|
|
|
default: \
|
|
|
|
assert(0); \
|
|
|
|
break; \
|
2006-05-10 07:53:23 +08:00
|
|
|
} \
|
2009-05-11 06:39:33 +08:00
|
|
|
} while (0);
|
2006-05-10 07:53:23 +08:00
|
|
|
|
2014-02-10 14:04:30 +08:00
|
|
|
#define convert_to_explicit_type_ex(pzv, str_type) \
|
|
|
|
if (Z_TYPE_P(pzv) != str_type) { \
|
|
|
|
convert_to_explicit_type(pzv, str_type); \
|
2006-05-10 07:53:23 +08:00
|
|
|
}
|
2000-04-19 06:23:28 +08:00
|
|
|
|
2018-07-06 06:26:43 +08:00
|
|
|
#define convert_to_boolean_ex(pzv) do { \
|
|
|
|
if (Z_TYPE_INFO_P(pzv) > IS_TRUE) { \
|
|
|
|
convert_to_boolean(pzv); \
|
|
|
|
} else if (Z_TYPE_INFO_P(pzv) < IS_FALSE) { \
|
|
|
|
ZVAL_FALSE(pzv); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2014-04-30 22:32:42 +08:00
|
|
|
#define convert_to_long_ex(pzv) convert_to_ex_master(pzv, long, IS_LONG)
|
|
|
|
#define convert_to_double_ex(pzv) convert_to_ex_master(pzv, double, IS_DOUBLE)
|
|
|
|
#define convert_to_string_ex(pzv) convert_to_ex_master(pzv, string, IS_STRING)
|
|
|
|
#define convert_to_array_ex(pzv) convert_to_ex_master(pzv, array, IS_ARRAY)
|
|
|
|
#define convert_to_object_ex(pzv) convert_to_ex_master(pzv, object, IS_OBJECT)
|
|
|
|
#define convert_to_null_ex(pzv) convert_to_ex_master(pzv, null, IS_NULL)
|
2014-02-10 14:04:30 +08:00
|
|
|
|
|
|
|
#define convert_scalar_to_number_ex(pzv) \
|
|
|
|
if (Z_TYPE_P(pzv)!=IS_LONG && Z_TYPE_P(pzv)!=IS_DOUBLE) { \
|
2014-12-14 06:06:14 +08:00
|
|
|
convert_scalar_to_number(pzv); \
|
1999-10-15 14:25:42 +08:00
|
|
|
}
|
|
|
|
|
2019-02-24 17:21:25 +08:00
|
|
|
#if defined(ZEND_WIN32) && !defined(ZTS) && defined(_MSC_VER)
|
2015-03-29 13:10:39 +08:00
|
|
|
/* This performance improvement of tolower() on Windows gives 10-18% on bench.php */
|
2006-12-05 02:37:15 +08:00
|
|
|
#define ZEND_USE_TOLOWER_L 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ZEND_USE_TOLOWER_L
|
2007-07-21 08:35:15 +08:00
|
|
|
ZEND_API void zend_update_current_locale(void);
|
2006-12-05 02:37:15 +08:00
|
|
|
#else
|
|
|
|
#define zend_update_current_locale()
|
|
|
|
#endif
|
|
|
|
|
2013-02-11 20:53:27 +08:00
|
|
|
/* The offset in bytes between the value and type fields of a zval */
|
|
|
|
#define ZVAL_OFFSETOF_TYPE \
|
2014-04-03 19:26:23 +08:00
|
|
|
(offsetof(zval, u1.type_info) - offsetof(zval, value))
|
2013-02-11 20:53:27 +08:00
|
|
|
|
2019-09-29 17:33:45 +08:00
|
|
|
#if defined(HAVE_ASM_GOTO) && !__has_feature(memory_sanitizer)
|
|
|
|
# define ZEND_USE_ASM_ARITHMETIC 1
|
|
|
|
#else
|
|
|
|
# define ZEND_USE_ASM_ARITHMETIC 0
|
|
|
|
#endif
|
|
|
|
|
2015-03-17 23:53:19 +08:00
|
|
|
static zend_always_inline void fast_long_increment_function(zval *op1)
|
2011-05-23 16:05:44 +08:00
|
|
|
{
|
2019-09-29 17:33:45 +08:00
|
|
|
#if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2018-05-08 05:00:59 +08:00
|
|
|
"addl $1,(%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l1\n"
|
|
|
|
:
|
|
|
|
: "r"(&op1->value)
|
|
|
|
: "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2018-05-08 05:00:59 +08:00
|
|
|
"addq $1,(%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l1\n"
|
|
|
|
:
|
|
|
|
: "r"(&op1->value)
|
|
|
|
: "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
|
speed up increment and decrement operators with overflow detection
On A72, google-benchmark measure before and after the patch:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
BM_inc_before 6.54 ns 6.54 ns 106985447
BM_dec_before 6.54 ns 6.54 ns 107011667
BM_inc_after 4.36 ns 4.36 ns 160525864
BM_dec_after 4.36 ns 4.36 ns 160524243
Before the patch:
fast_long_add_function:
ldr x0, [x1]
add x2, x0, 1
cmp x2, x0
blt .L11
str x2, [x1]
ret
.L11:
mov x0, 4890909195324358656
mov w2, 5
str x0, [x1]
str w2, [x1, 8]
ret
With the patch:
fast_long_add_function:
ldr x5, [x1]
adds x5,x5,1
bvs .L2
str x5, [x1]
ret
.L2:
mov x0, 4890909195324358656
mov w2, 5
str x0, [x1]
str w2, [x1, 8]
ret
php$ ./sapi/cli/php Zend/bench.php
Base: Patch:
simple 0.091 simple 0.091
simplecall 0.014 simplecall 0.014
simpleucall 0.041 simpleucall 0.041
simpleudcall 0.045 simpleudcall 0.045
mandel 0.193 mandel 0.193
mandel2 0.229 mandel2 0.229
ackermann(7) 0.044 ackermann(7) 0.044
ary(50000) 0.010 ary(50000) 0.010
ary2(50000) 0.008 ary2(50000) 0.008
ary3(2000) 0.096 ary3(2000) 0.102
fibo(30) 0.149 fibo(30) 0.148
hash1(50000) 0.016 hash1(50000) 0.016
hash2(500) 0.020 hash2(500) 0.020
heapsort(20000) 0.055 heapsort(20000) 0.055
matrix(20) 0.057 matrix(20) 0.057
nestedloop(12) 0.091 nestedloop(12) 0.091
sieve(30) 0.032 sieve(30) 0.032
strcat(200000) 0.010 strcat(200000) 0.010
------------------------ ------------------------
Total 1.199 Total 1.204
php$ ./sapi/cli/php Zend/micro_bench.php
Base: Patch:
empty_loop 0.051 empty_loop 0.050
func() 0.181 0.130 func() 0.181 0.131
undef_func() 0.186 0.135 undef_func() 0.186 0.136
int_func() 0.116 0.064 int_func() 0.116 0.065
$x = self::$x 0.235 0.183 $x = self::$x 0.229 0.179
self::$x = 0 0.198 0.147 self::$x = 0 0.199 0.148
isset(self::$x) 0.229 0.178 isset(self::$x) 0.225 0.174
empty(self::$x) 0.231 0.180 empty(self::$x) 0.227 0.177
$x = Foo::$x 0.144 0.093 $x = Foo::$x 0.142 0.092
Foo::$x = 0 0.107 0.056 Foo::$x = 0 0.105 0.054
isset(Foo::$x) 0.140 0.088 isset(Foo::$x) 0.140 0.089
empty(Foo::$x) 0.148 0.097 empty(Foo::$x) 0.144 0.094
self::f() 0.238 0.187 self::f() 0.240 0.190
Foo::f() 0.209 0.158 Foo::f() 0.201 0.150
$x = $this->x 0.123 0.072 $x = $this->x 0.120 0.070
$this->x = 0 0.124 0.073 $this->x = 0 0.124 0.074
$this->x += 2 0.151 0.099 $this->x += 2 0.151 0.101
++$this->x 0.137 0.086 ++$this->x 0.139 0.088
--$this->x 0.137 0.086 --$this->x 0.137 0.087
$this->x++ 0.170 0.119 $this->x++ 0.172 0.122
$this->x-- 0.171 0.119 $this->x-- 0.172 0.122
isset($this->x) 0.170 0.119 isset($this->x) 0.170 0.120
empty($this->x) 0.179 0.128 empty($this->x) 0.179 0.129
$this->f() 0.194 0.143 $this->f() 0.194 0.144
$x = Foo::TEST 0.188 0.137 $x = Foo::TEST 0.188 0.138
new Foo() 0.482 0.431 new Foo() 0.482 0.432
$x = TEST 0.109 0.058 $x = TEST 0.109 0.059
$x = $_GET 0.190 0.138 $x = $_GET 0.188 0.137
$x = $GLOBALS['v'] 0.242 0.191 $x = $GLOBALS['v'] 0.246 0.196
$x = $hash['v'] 0.196 0.145 $x = $hash['v'] 0.192 0.142
$x = $str[0] 0.146 0.094 $x = $str[0] 0.142 0.092
$x = $a ?: null 0.144 0.093 $x = $a ?: null 0.144 0.094
$x = $f ?: tmp 0.174 0.123 $x = $f ?: tmp 0.174 0.124
$x = $f ? $f : $a 0.153 0.101 $x = $f ? $f : $a 0.153 0.102
$x = $f ? $f : tmp 0.148 0.097 $x = $f ? $f : tmp 0.148 0.098
------------------------ ------------------------
Total 6.143 Total 6.108
2019-04-24 04:18:39 +08:00
|
|
|
__asm__ goto (
|
|
|
|
"ldr x5, [%0]\n\t"
|
|
|
|
"adds x5, x5, 1\n\t"
|
|
|
|
"bvs %l1\n"
|
|
|
|
"str x5, [%0]"
|
|
|
|
:
|
|
|
|
: "r"(&op1->value)
|
|
|
|
: "x5", "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
|
2017-11-28 17:37:00 +08:00
|
|
|
#elif PHP_HAVE_BUILTIN_SADDL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
|
2016-08-10 07:28:13 +08:00
|
|
|
long lresult;
|
|
|
|
if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), 1, &lresult))) {
|
|
|
|
/* switch to double */
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
|
|
|
|
} else {
|
|
|
|
Z_LVAL_P(op1) = lresult;
|
|
|
|
}
|
2016-08-11 19:57:30 +08:00
|
|
|
#elif PHP_HAVE_BUILTIN_SADDLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
|
2016-08-10 07:28:13 +08:00
|
|
|
long long llresult;
|
|
|
|
if (UNEXPECTED(__builtin_saddll_overflow(Z_LVAL_P(op1), 1, &llresult))) {
|
|
|
|
/* switch to double */
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
|
|
|
|
} else {
|
|
|
|
Z_LVAL_P(op1) = llresult;
|
|
|
|
}
|
2011-07-22 19:42:15 +08:00
|
|
|
#else
|
2015-03-17 23:53:19 +08:00
|
|
|
if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MAX)) {
|
|
|
|
/* switch to double */
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
|
|
|
|
} else {
|
|
|
|
Z_LVAL_P(op1)++;
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
2015-03-17 23:53:19 +08:00
|
|
|
#endif
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2015-03-17 23:53:19 +08:00
|
|
|
static zend_always_inline void fast_long_decrement_function(zval *op1)
|
2011-05-23 16:05:44 +08:00
|
|
|
{
|
2019-09-29 17:33:45 +08:00
|
|
|
#if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2018-05-08 05:00:59 +08:00
|
|
|
"subl $1,(%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l1\n"
|
|
|
|
:
|
|
|
|
: "r"(&op1->value)
|
|
|
|
: "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2018-05-08 05:00:59 +08:00
|
|
|
"subq $1,(%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l1\n"
|
|
|
|
:
|
|
|
|
: "r"(&op1->value)
|
|
|
|
: "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
|
speed up increment and decrement operators with overflow detection
On A72, google-benchmark measure before and after the patch:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
BM_inc_before 6.54 ns 6.54 ns 106985447
BM_dec_before 6.54 ns 6.54 ns 107011667
BM_inc_after 4.36 ns 4.36 ns 160525864
BM_dec_after 4.36 ns 4.36 ns 160524243
Before the patch:
fast_long_add_function:
ldr x0, [x1]
add x2, x0, 1
cmp x2, x0
blt .L11
str x2, [x1]
ret
.L11:
mov x0, 4890909195324358656
mov w2, 5
str x0, [x1]
str w2, [x1, 8]
ret
With the patch:
fast_long_add_function:
ldr x5, [x1]
adds x5,x5,1
bvs .L2
str x5, [x1]
ret
.L2:
mov x0, 4890909195324358656
mov w2, 5
str x0, [x1]
str w2, [x1, 8]
ret
php$ ./sapi/cli/php Zend/bench.php
Base: Patch:
simple 0.091 simple 0.091
simplecall 0.014 simplecall 0.014
simpleucall 0.041 simpleucall 0.041
simpleudcall 0.045 simpleudcall 0.045
mandel 0.193 mandel 0.193
mandel2 0.229 mandel2 0.229
ackermann(7) 0.044 ackermann(7) 0.044
ary(50000) 0.010 ary(50000) 0.010
ary2(50000) 0.008 ary2(50000) 0.008
ary3(2000) 0.096 ary3(2000) 0.102
fibo(30) 0.149 fibo(30) 0.148
hash1(50000) 0.016 hash1(50000) 0.016
hash2(500) 0.020 hash2(500) 0.020
heapsort(20000) 0.055 heapsort(20000) 0.055
matrix(20) 0.057 matrix(20) 0.057
nestedloop(12) 0.091 nestedloop(12) 0.091
sieve(30) 0.032 sieve(30) 0.032
strcat(200000) 0.010 strcat(200000) 0.010
------------------------ ------------------------
Total 1.199 Total 1.204
php$ ./sapi/cli/php Zend/micro_bench.php
Base: Patch:
empty_loop 0.051 empty_loop 0.050
func() 0.181 0.130 func() 0.181 0.131
undef_func() 0.186 0.135 undef_func() 0.186 0.136
int_func() 0.116 0.064 int_func() 0.116 0.065
$x = self::$x 0.235 0.183 $x = self::$x 0.229 0.179
self::$x = 0 0.198 0.147 self::$x = 0 0.199 0.148
isset(self::$x) 0.229 0.178 isset(self::$x) 0.225 0.174
empty(self::$x) 0.231 0.180 empty(self::$x) 0.227 0.177
$x = Foo::$x 0.144 0.093 $x = Foo::$x 0.142 0.092
Foo::$x = 0 0.107 0.056 Foo::$x = 0 0.105 0.054
isset(Foo::$x) 0.140 0.088 isset(Foo::$x) 0.140 0.089
empty(Foo::$x) 0.148 0.097 empty(Foo::$x) 0.144 0.094
self::f() 0.238 0.187 self::f() 0.240 0.190
Foo::f() 0.209 0.158 Foo::f() 0.201 0.150
$x = $this->x 0.123 0.072 $x = $this->x 0.120 0.070
$this->x = 0 0.124 0.073 $this->x = 0 0.124 0.074
$this->x += 2 0.151 0.099 $this->x += 2 0.151 0.101
++$this->x 0.137 0.086 ++$this->x 0.139 0.088
--$this->x 0.137 0.086 --$this->x 0.137 0.087
$this->x++ 0.170 0.119 $this->x++ 0.172 0.122
$this->x-- 0.171 0.119 $this->x-- 0.172 0.122
isset($this->x) 0.170 0.119 isset($this->x) 0.170 0.120
empty($this->x) 0.179 0.128 empty($this->x) 0.179 0.129
$this->f() 0.194 0.143 $this->f() 0.194 0.144
$x = Foo::TEST 0.188 0.137 $x = Foo::TEST 0.188 0.138
new Foo() 0.482 0.431 new Foo() 0.482 0.432
$x = TEST 0.109 0.058 $x = TEST 0.109 0.059
$x = $_GET 0.190 0.138 $x = $_GET 0.188 0.137
$x = $GLOBALS['v'] 0.242 0.191 $x = $GLOBALS['v'] 0.246 0.196
$x = $hash['v'] 0.196 0.145 $x = $hash['v'] 0.192 0.142
$x = $str[0] 0.146 0.094 $x = $str[0] 0.142 0.092
$x = $a ?: null 0.144 0.093 $x = $a ?: null 0.144 0.094
$x = $f ?: tmp 0.174 0.123 $x = $f ?: tmp 0.174 0.124
$x = $f ? $f : $a 0.153 0.101 $x = $f ? $f : $a 0.153 0.102
$x = $f ? $f : tmp 0.148 0.097 $x = $f ? $f : tmp 0.148 0.098
------------------------ ------------------------
Total 6.143 Total 6.108
2019-04-24 04:18:39 +08:00
|
|
|
__asm__ goto (
|
|
|
|
"ldr x5, [%0]\n\t"
|
|
|
|
"subs x5 ,x5, 1\n\t"
|
|
|
|
"bvs %l1\n"
|
|
|
|
"str x5, [%0]"
|
|
|
|
:
|
|
|
|
: "r"(&op1->value)
|
|
|
|
: "x5", "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
|
2017-11-28 17:37:00 +08:00
|
|
|
#elif PHP_HAVE_BUILTIN_SSUBL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
|
2016-08-10 07:28:13 +08:00
|
|
|
long lresult;
|
|
|
|
if (UNEXPECTED(__builtin_ssubl_overflow(Z_LVAL_P(op1), 1, &lresult))) {
|
|
|
|
/* switch to double */
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
|
|
|
|
} else {
|
|
|
|
Z_LVAL_P(op1) = lresult;
|
|
|
|
}
|
2016-08-11 19:57:30 +08:00
|
|
|
#elif PHP_HAVE_BUILTIN_SSUBLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
|
2016-08-10 07:28:13 +08:00
|
|
|
long long llresult;
|
|
|
|
if (UNEXPECTED(__builtin_ssubll_overflow(Z_LVAL_P(op1), 1, &llresult))) {
|
|
|
|
/* switch to double */
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
|
|
|
|
} else {
|
|
|
|
Z_LVAL_P(op1) = llresult;
|
|
|
|
}
|
2011-05-23 16:05:44 +08:00
|
|
|
#else
|
2015-03-17 23:53:19 +08:00
|
|
|
if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MIN)) {
|
|
|
|
/* switch to double */
|
|
|
|
ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
|
|
|
|
} else {
|
|
|
|
Z_LVAL_P(op1)--;
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
2015-03-17 23:53:19 +08:00
|
|
|
#endif
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2015-03-17 23:53:19 +08:00
|
|
|
static zend_always_inline void fast_long_add_function(zval *result, zval *op1, zval *op2)
|
2011-05-23 16:05:44 +08:00
|
|
|
{
|
2019-09-29 17:33:45 +08:00
|
|
|
#if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2015-03-17 23:53:19 +08:00
|
|
|
"movl (%1), %%eax\n\t"
|
|
|
|
"addl (%2), %%eax\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l5\n\t"
|
2015-03-17 23:53:19 +08:00
|
|
|
"movl %%eax, (%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"movl %3, %c4(%0)\n"
|
2015-03-17 23:53:19 +08:00
|
|
|
:
|
|
|
|
: "r"(&result->value),
|
|
|
|
"r"(&op1->value),
|
|
|
|
"r"(&op2->value),
|
|
|
|
"n"(IS_LONG),
|
|
|
|
"n"(ZVAL_OFFSETOF_TYPE)
|
2017-11-28 17:37:00 +08:00
|
|
|
: "eax","cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2015-03-17 23:53:19 +08:00
|
|
|
"movq (%1), %%rax\n\t"
|
|
|
|
"addq (%2), %%rax\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l5\n\t"
|
2015-03-17 23:53:19 +08:00
|
|
|
"movq %%rax, (%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"movl %3, %c4(%0)\n"
|
2015-03-17 23:53:19 +08:00
|
|
|
:
|
|
|
|
: "r"(&result->value),
|
|
|
|
"r"(&op1->value),
|
|
|
|
"r"(&op2->value),
|
|
|
|
"n"(IS_LONG),
|
|
|
|
"n"(ZVAL_OFFSETOF_TYPE)
|
2017-11-28 17:37:00 +08:00
|
|
|
: "rax","cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
|
speed up add and sub operators with overflow detection
On A72, google-benchmark measure before and after the patch:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
BM_add_before 13.3 ns 13.3 ns 52626058
BM_sub_before 8.72 ns 8.72 ns 80259343
BM_add_after 4.80 ns 4.80 ns 145926004
BM_sub_after 4.80 ns 4.80 ns 145936496
Before the patch:
fast_long_add_function:
ldr x1, [x1]
ldr x2, [x2]
add x3, x1, x2
eor x4, x1, x2
tbz x4, #63, .L5
.L2:
mov w1, 4
str x3, [x0]
str w1, [x0, 8]
ret
.p2align 2
.L5:
eor x4, x1, x3
tbz x4, #63, .L2
scvtf d0, x1
scvtf d1, x2
mov w1, 5
str w1, [x0, 8]
fadd d0, d0, d1
str d0, [x0]
ret
With the patch:
fast_long_add_function:
ldr x5, [x1]
ldr x6, [x2]
adds x5, x5, x6
bvs .L2
mov w6, 4
str x5, [x0]
str w6, [x0, 8]
ret
.L2:
ldr x1, [x1]
mov w3, 5
ldr x2, [x2]
str w3, [x0, 8]
scvtf d0, x1
scvtf d1, x2
fadd d0, d0, d1
str d0, [x0]
ret
php$ ./sapi/cli/php Zend/bench.php
Base: Patch:
simple 0.091 simple 0.091
simplecall 0.014 simplecall 0.014
simpleucall 0.041 simpleucall 0.041
simpleudcall 0.045 simpleudcall 0.045
mandel 0.193 mandel 0.193
mandel2 0.229 mandel2 0.229
ackermann(7) 0.044 ackermann(7) 0.044
ary(50000) 0.010 ary(50000) 0.010
ary2(50000) 0.008 ary2(50000) 0.008
ary3(2000) 0.096 ary3(2000) 0.095
fibo(30) 0.149 fibo(30) 0.148
hash1(50000) 0.016 hash1(50000) 0.016
hash2(500) 0.020 hash2(500) 0.020
heapsort(20000) 0.055 heapsort(20000) 0.054
matrix(20) 0.057 matrix(20) 0.057
nestedloop(12) 0.091 nestedloop(12) 0.091
sieve(30) 0.032 sieve(30) 0.032
strcat(200000) 0.010 strcat(200000) 0.010
------------------------ ------------------------
Total 1.199 Total 1.197
php$ ./sapi/cli/php Zend/micro_bench.php
Base: Patch:
empty_loop 0.051 empty_loop 0.051
func() 0.181 0.130 func() 0.181 0.130
undef_func() 0.186 0.135 undef_func() 0.186 0.135
int_func() 0.116 0.064 int_func() 0.116 0.064
$x = self::$x 0.235 0.183 $x = self::$x 0.233 0.182
self::$x = 0 0.198 0.147 self::$x = 0 0.198 0.147
isset(self::$x) 0.229 0.178 isset(self::$x) 0.229 0.178
empty(self::$x) 0.231 0.180 empty(self::$x) 0.231 0.180
$x = Foo::$x 0.144 0.093 $x = Foo::$x 0.144 0.093
Foo::$x = 0 0.107 0.056 Foo::$x = 0 0.107 0.056
isset(Foo::$x) 0.140 0.088 isset(Foo::$x) 0.140 0.088
empty(Foo::$x) 0.148 0.097 empty(Foo::$x) 0.148 0.097
self::f() 0.238 0.187 self::f() 0.238 0.187
Foo::f() 0.209 0.158 Foo::f() 0.209 0.158
$x = $this->x 0.123 0.072 $x = $this->x 0.123 0.072
$this->x = 0 0.124 0.073 $this->x = 0 0.124 0.073
$this->x += 2 0.151 0.099 $this->x += 2 0.153 0.101
++$this->x 0.137 0.086 ++$this->x 0.138 0.086
--$this->x 0.137 0.086 --$this->x 0.138 0.086
$this->x++ 0.170 0.119 $this->x++ 0.172 0.121
$this->x-- 0.171 0.119 $this->x-- 0.172 0.121
isset($this->x) 0.170 0.119 isset($this->x) 0.170 0.119
empty($this->x) 0.179 0.128 empty($this->x) 0.179 0.128
$this->f() 0.194 0.143 $this->f() 0.194 0.143
$x = Foo::TEST 0.188 0.137 $x = Foo::TEST 0.188 0.136
new Foo() 0.482 0.431 new Foo() 0.479 0.427
$x = TEST 0.109 0.058 $x = TEST 0.109 0.058
$x = $_GET 0.190 0.138 $x = $_GET 0.190 0.139
$x = $GLOBALS['v'] 0.242 0.191 $x = $GLOBALS['v'] 0.242 0.191
$x = $hash['v'] 0.196 0.145 $x = $hash['v'] 0.196 0.145
$x = $str[0] 0.146 0.094 $x = $str[0] 0.145 0.094
$x = $a ?: null 0.144 0.093 $x = $a ?: null 0.144 0.093
$x = $f ?: tmp 0.174 0.123 $x = $f ?: tmp 0.174 0.123
$x = $f ? $f : $a 0.153 0.101 $x = $f ? $f : $a 0.153 0.101
$x = $f ? $f : tmp 0.148 0.097 $x = $f ? $f : tmp 0.148 0.097
------------------------ ------------------------
Total 6.143 Total 6.143
2019-04-25 22:44:02 +08:00
|
|
|
__asm__ goto(
|
|
|
|
"ldr x5, [%1]\n\t"
|
|
|
|
"ldr x6, [%2]\n\t"
|
|
|
|
"adds x5, x5, x6\n\t"
|
|
|
|
"bvs %l5\n\t"
|
|
|
|
"mov w6, %3\n\t"
|
|
|
|
"str x5, [%0]\n\t"
|
|
|
|
"str w6, [%0, %c4]\n"
|
|
|
|
:
|
|
|
|
: "r"(&result->value),
|
|
|
|
"r"(&op1->value),
|
|
|
|
"r"(&op2->value),
|
|
|
|
"n"(IS_LONG),
|
|
|
|
"n"(ZVAL_OFFSETOF_TYPE)
|
|
|
|
: "x5", "x6", "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
|
2017-11-28 17:37:00 +08:00
|
|
|
#elif PHP_HAVE_BUILTIN_SADDL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
|
|
|
|
long lresult;
|
|
|
|
if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &lresult))) {
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
|
|
|
|
} else {
|
|
|
|
ZVAL_LONG(result, lresult);
|
|
|
|
}
|
|
|
|
#elif PHP_HAVE_BUILTIN_SADDLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
|
|
|
|
long long llresult;
|
|
|
|
if (UNEXPECTED(__builtin_saddll_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &llresult))) {
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
|
|
|
|
} else {
|
|
|
|
ZVAL_LONG(result, llresult);
|
|
|
|
}
|
2011-05-23 16:05:44 +08:00
|
|
|
#else
|
2015-03-17 23:53:19 +08:00
|
|
|
/*
|
|
|
|
* 'result' may alias with op1 or op2, so we need to
|
|
|
|
* ensure that 'result' is not updated until after we
|
|
|
|
* have read the values of op1 and op2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK)
|
|
|
|
&& (Z_LVAL_P(op1) & LONG_SIGN_MASK) != ((Z_LVAL_P(op1) + Z_LVAL_P(op2)) & LONG_SIGN_MASK))) {
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
|
|
|
|
} else {
|
|
|
|
ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
|
|
|
|
}
|
2011-05-23 16:05:44 +08:00
|
|
|
#endif
|
2015-03-17 23:53:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static zend_always_inline int fast_add_function(zval *result, zval *op1, zval *op2)
|
|
|
|
{
|
|
|
|
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
|
|
|
|
fast_long_add_function(result, op1, op2);
|
2011-05-23 16:05:44 +08:00
|
|
|
return SUCCESS;
|
|
|
|
} else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
|
2014-04-03 19:26:23 +08:00
|
|
|
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
|
2011-05-23 16:05:44 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
} else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
|
2014-04-03 19:26:23 +08:00
|
|
|
ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
|
2011-05-23 16:05:44 +08:00
|
|
|
return SUCCESS;
|
|
|
|
} else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
|
2014-04-03 19:26:23 +08:00
|
|
|
ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2)));
|
2011-05-23 16:05:44 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
2014-12-14 06:06:14 +08:00
|
|
|
return add_function(result, op1, op2);
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2015-03-17 23:53:19 +08:00
|
|
|
static zend_always_inline void fast_long_sub_function(zval *result, zval *op1, zval *op2)
|
2011-05-23 16:05:44 +08:00
|
|
|
{
|
2019-09-29 17:33:45 +08:00
|
|
|
#if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2015-03-17 23:53:19 +08:00
|
|
|
"movl (%1), %%eax\n\t"
|
|
|
|
"subl (%2), %%eax\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l5\n\t"
|
2015-03-17 23:53:19 +08:00
|
|
|
"movl %%eax, (%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"movl %3, %c4(%0)\n"
|
2015-03-17 23:53:19 +08:00
|
|
|
:
|
|
|
|
: "r"(&result->value),
|
|
|
|
"r"(&op1->value),
|
|
|
|
"r"(&op2->value),
|
|
|
|
"n"(IS_LONG),
|
|
|
|
"n"(ZVAL_OFFSETOF_TYPE)
|
2017-11-28 17:37:00 +08:00
|
|
|
: "eax","cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
|
2017-11-28 17:37:00 +08:00
|
|
|
__asm__ goto(
|
2015-03-17 23:53:19 +08:00
|
|
|
"movq (%1), %%rax\n\t"
|
|
|
|
"subq (%2), %%rax\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"jo %l5\n\t"
|
2015-03-17 23:53:19 +08:00
|
|
|
"movq %%rax, (%0)\n\t"
|
2017-11-28 17:37:00 +08:00
|
|
|
"movl %3, %c4(%0)\n"
|
2015-03-17 23:53:19 +08:00
|
|
|
:
|
|
|
|
: "r"(&result->value),
|
|
|
|
"r"(&op1->value),
|
|
|
|
"r"(&op2->value),
|
|
|
|
"n"(IS_LONG),
|
|
|
|
"n"(ZVAL_OFFSETOF_TYPE)
|
2017-11-28 17:37:00 +08:00
|
|
|
: "rax","cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
|
2019-09-29 17:33:45 +08:00
|
|
|
#elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
|
speed up add and sub operators with overflow detection
On A72, google-benchmark measure before and after the patch:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
BM_add_before 13.3 ns 13.3 ns 52626058
BM_sub_before 8.72 ns 8.72 ns 80259343
BM_add_after 4.80 ns 4.80 ns 145926004
BM_sub_after 4.80 ns 4.80 ns 145936496
Before the patch:
fast_long_add_function:
ldr x1, [x1]
ldr x2, [x2]
add x3, x1, x2
eor x4, x1, x2
tbz x4, #63, .L5
.L2:
mov w1, 4
str x3, [x0]
str w1, [x0, 8]
ret
.p2align 2
.L5:
eor x4, x1, x3
tbz x4, #63, .L2
scvtf d0, x1
scvtf d1, x2
mov w1, 5
str w1, [x0, 8]
fadd d0, d0, d1
str d0, [x0]
ret
With the patch:
fast_long_add_function:
ldr x5, [x1]
ldr x6, [x2]
adds x5, x5, x6
bvs .L2
mov w6, 4
str x5, [x0]
str w6, [x0, 8]
ret
.L2:
ldr x1, [x1]
mov w3, 5
ldr x2, [x2]
str w3, [x0, 8]
scvtf d0, x1
scvtf d1, x2
fadd d0, d0, d1
str d0, [x0]
ret
php$ ./sapi/cli/php Zend/bench.php
Base: Patch:
simple 0.091 simple 0.091
simplecall 0.014 simplecall 0.014
simpleucall 0.041 simpleucall 0.041
simpleudcall 0.045 simpleudcall 0.045
mandel 0.193 mandel 0.193
mandel2 0.229 mandel2 0.229
ackermann(7) 0.044 ackermann(7) 0.044
ary(50000) 0.010 ary(50000) 0.010
ary2(50000) 0.008 ary2(50000) 0.008
ary3(2000) 0.096 ary3(2000) 0.095
fibo(30) 0.149 fibo(30) 0.148
hash1(50000) 0.016 hash1(50000) 0.016
hash2(500) 0.020 hash2(500) 0.020
heapsort(20000) 0.055 heapsort(20000) 0.054
matrix(20) 0.057 matrix(20) 0.057
nestedloop(12) 0.091 nestedloop(12) 0.091
sieve(30) 0.032 sieve(30) 0.032
strcat(200000) 0.010 strcat(200000) 0.010
------------------------ ------------------------
Total 1.199 Total 1.197
php$ ./sapi/cli/php Zend/micro_bench.php
Base: Patch:
empty_loop 0.051 empty_loop 0.051
func() 0.181 0.130 func() 0.181 0.130
undef_func() 0.186 0.135 undef_func() 0.186 0.135
int_func() 0.116 0.064 int_func() 0.116 0.064
$x = self::$x 0.235 0.183 $x = self::$x 0.233 0.182
self::$x = 0 0.198 0.147 self::$x = 0 0.198 0.147
isset(self::$x) 0.229 0.178 isset(self::$x) 0.229 0.178
empty(self::$x) 0.231 0.180 empty(self::$x) 0.231 0.180
$x = Foo::$x 0.144 0.093 $x = Foo::$x 0.144 0.093
Foo::$x = 0 0.107 0.056 Foo::$x = 0 0.107 0.056
isset(Foo::$x) 0.140 0.088 isset(Foo::$x) 0.140 0.088
empty(Foo::$x) 0.148 0.097 empty(Foo::$x) 0.148 0.097
self::f() 0.238 0.187 self::f() 0.238 0.187
Foo::f() 0.209 0.158 Foo::f() 0.209 0.158
$x = $this->x 0.123 0.072 $x = $this->x 0.123 0.072
$this->x = 0 0.124 0.073 $this->x = 0 0.124 0.073
$this->x += 2 0.151 0.099 $this->x += 2 0.153 0.101
++$this->x 0.137 0.086 ++$this->x 0.138 0.086
--$this->x 0.137 0.086 --$this->x 0.138 0.086
$this->x++ 0.170 0.119 $this->x++ 0.172 0.121
$this->x-- 0.171 0.119 $this->x-- 0.172 0.121
isset($this->x) 0.170 0.119 isset($this->x) 0.170 0.119
empty($this->x) 0.179 0.128 empty($this->x) 0.179 0.128
$this->f() 0.194 0.143 $this->f() 0.194 0.143
$x = Foo::TEST 0.188 0.137 $x = Foo::TEST 0.188 0.136
new Foo() 0.482 0.431 new Foo() 0.479 0.427
$x = TEST 0.109 0.058 $x = TEST 0.109 0.058
$x = $_GET 0.190 0.138 $x = $_GET 0.190 0.139
$x = $GLOBALS['v'] 0.242 0.191 $x = $GLOBALS['v'] 0.242 0.191
$x = $hash['v'] 0.196 0.145 $x = $hash['v'] 0.196 0.145
$x = $str[0] 0.146 0.094 $x = $str[0] 0.145 0.094
$x = $a ?: null 0.144 0.093 $x = $a ?: null 0.144 0.093
$x = $f ?: tmp 0.174 0.123 $x = $f ?: tmp 0.174 0.123
$x = $f ? $f : $a 0.153 0.101 $x = $f ? $f : $a 0.153 0.101
$x = $f ? $f : tmp 0.148 0.097 $x = $f ? $f : tmp 0.148 0.097
------------------------ ------------------------
Total 6.143 Total 6.143
2019-04-25 22:44:02 +08:00
|
|
|
__asm__ goto(
|
|
|
|
"ldr x5, [%1]\n\t"
|
|
|
|
"ldr x6, [%2]\n\t"
|
|
|
|
"subs x5, x5, x6\n\t"
|
|
|
|
"bvs %l5\n\t"
|
|
|
|
"mov w6, %3\n\t"
|
|
|
|
"str x5, [%0]\n\t"
|
|
|
|
"str w6, [%0, %c4]\n"
|
|
|
|
:
|
|
|
|
: "r"(&result->value),
|
|
|
|
"r"(&op1->value),
|
|
|
|
"r"(&op2->value),
|
|
|
|
"n"(IS_LONG),
|
|
|
|
"n"(ZVAL_OFFSETOF_TYPE)
|
|
|
|
: "x5", "x6", "cc", "memory"
|
|
|
|
: overflow);
|
|
|
|
return;
|
|
|
|
overflow: ZEND_ATTRIBUTE_COLD_LABEL
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
|
2017-11-28 17:37:00 +08:00
|
|
|
#elif PHP_HAVE_BUILTIN_SSUBL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
|
|
|
|
long lresult;
|
|
|
|
if (UNEXPECTED(__builtin_ssubl_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &lresult))) {
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
|
|
|
|
} else {
|
|
|
|
ZVAL_LONG(result, lresult);
|
|
|
|
}
|
|
|
|
#elif PHP_HAVE_BUILTIN_SSUBLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
|
|
|
|
long long llresult;
|
|
|
|
if (UNEXPECTED(__builtin_ssubll_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &llresult))) {
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
|
|
|
|
} else {
|
|
|
|
ZVAL_LONG(result, llresult);
|
|
|
|
}
|
2011-05-23 16:05:44 +08:00
|
|
|
#else
|
2015-03-17 23:53:19 +08:00
|
|
|
ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
|
2011-05-23 16:05:44 +08:00
|
|
|
|
2015-03-17 23:53:19 +08:00
|
|
|
if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(op2) & LONG_SIGN_MASK)
|
|
|
|
&& (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(result) & LONG_SIGN_MASK))) {
|
|
|
|
ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
2015-03-17 23:53:19 +08:00
|
|
|
#endif
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static zend_always_inline int fast_div_function(zval *result, zval *op1, zval *op2)
|
2011-05-23 16:05:44 +08:00
|
|
|
{
|
2014-12-14 06:06:14 +08:00
|
|
|
return div_function(result, op1, op2);
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2017-12-04 22:17:02 +08:00
|
|
|
static zend_always_inline int zend_fast_equal_strings(zend_string *s1, zend_string *s2)
|
|
|
|
{
|
|
|
|
if (s1 == s2) {
|
|
|
|
return 1;
|
|
|
|
} else if (ZSTR_VAL(s1)[0] > '9' || ZSTR_VAL(s2)[0] > '9') {
|
|
|
|
return zend_string_equal_content(s1, s2);
|
|
|
|
} else {
|
2018-01-16 22:24:00 +08:00
|
|
|
return zendi_smart_streq(s1, s2);
|
2017-12-04 22:17:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-22 11:35:25 +08:00
|
|
|
static zend_always_inline int fast_equal_check_function(zval *op1, zval *op2)
|
2011-05-23 16:05:44 +08:00
|
|
|
{
|
|
|
|
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
|
|
|
|
return Z_LVAL_P(op1) == Z_LVAL_P(op2);
|
|
|
|
} else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
|
|
|
|
return ((double)Z_LVAL_P(op1)) == Z_DVAL_P(op2);
|
|
|
|
}
|
|
|
|
} else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
|
|
|
|
return Z_DVAL_P(op1) == Z_DVAL_P(op2);
|
|
|
|
} else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
|
|
|
|
return Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2));
|
|
|
|
}
|
2014-06-05 23:14:47 +08:00
|
|
|
} else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
|
2017-12-04 22:17:02 +08:00
|
|
|
return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
|
2014-06-05 23:14:47 +08:00
|
|
|
}
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
2019-10-07 22:57:49 +08:00
|
|
|
return zend_compare(op1, op2) == 0;
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2014-12-27 16:11:40 +08:00
|
|
|
static zend_always_inline int fast_equal_check_long(zval *op1, zval *op2)
|
|
|
|
{
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
|
|
|
|
return Z_LVAL_P(op1) == Z_LVAL_P(op2);
|
|
|
|
}
|
2019-10-07 22:57:49 +08:00
|
|
|
return zend_compare(op1, op2) == 0;
|
2014-12-27 16:11:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static zend_always_inline int fast_equal_check_string(zval *op1, zval *op2)
|
|
|
|
{
|
|
|
|
if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
|
2017-12-04 22:17:02 +08:00
|
|
|
return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
|
2014-12-27 16:11:40 +08:00
|
|
|
}
|
2019-10-07 22:57:49 +08:00
|
|
|
return zend_compare(op1, op2) == 0;
|
2014-12-27 16:11:40 +08:00
|
|
|
}
|
|
|
|
|
2019-04-04 23:18:12 +08:00
|
|
|
static zend_always_inline zend_bool fast_is_identical_function(zval *op1, zval *op2)
|
2014-04-30 22:32:42 +08:00
|
|
|
{
|
|
|
|
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
|
2015-10-07 04:48:12 +08:00
|
|
|
return 0;
|
|
|
|
} else if (Z_TYPE_P(op1) <= IS_TRUE) {
|
2015-05-15 06:11:29 +08:00
|
|
|
return 1;
|
2014-04-30 22:32:42 +08:00
|
|
|
}
|
2015-10-07 04:48:12 +08:00
|
|
|
return zend_is_identical(op1, op2);
|
2014-04-30 22:32:42 +08:00
|
|
|
}
|
|
|
|
|
2019-04-04 23:18:12 +08:00
|
|
|
static zend_always_inline zend_bool fast_is_not_identical_function(zval *op1, zval *op2)
|
2014-04-30 22:32:42 +08:00
|
|
|
{
|
|
|
|
if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
|
2015-10-07 04:48:12 +08:00
|
|
|
return 1;
|
|
|
|
} else if (Z_TYPE_P(op1) <= IS_TRUE) {
|
2015-05-15 06:11:29 +08:00
|
|
|
return 0;
|
2014-04-30 22:32:42 +08:00
|
|
|
}
|
2015-10-07 04:48:12 +08:00
|
|
|
return !zend_is_identical(op1, op2);
|
2011-05-23 16:05:44 +08:00
|
|
|
}
|
|
|
|
|
2014-09-20 05:22:26 +08:00
|
|
|
/* buf points to the END of the buffer */
|
|
|
|
static zend_always_inline char *zend_print_ulong_to_buf(char *buf, zend_ulong num) {
|
|
|
|
*buf = '\0';
|
|
|
|
do {
|
|
|
|
*--buf = (char) (num % 10) + '0';
|
|
|
|
num /= 10;
|
|
|
|
} while (num > 0);
|
|
|
|
return buf;
|
|
|
|
}
|
2014-05-23 19:10:50 +08:00
|
|
|
|
|
|
|
/* buf points to the END of the buffer */
|
2014-09-20 05:22:26 +08:00
|
|
|
static zend_always_inline char *zend_print_long_to_buf(char *buf, zend_long num) {
|
|
|
|
if (num < 0) {
|
|
|
|
char *result = zend_print_ulong_to_buf(buf, ~((zend_ulong) num) + 1);
|
|
|
|
*--result = '-';
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return zend_print_ulong_to_buf(buf, num);
|
|
|
|
}
|
|
|
|
}
|
2014-05-23 19:10:50 +08:00
|
|
|
|
2015-03-14 02:10:09 +08:00
|
|
|
ZEND_API zend_string* ZEND_FASTCALL zend_long_to_str(zend_long num);
|
2014-05-23 19:10:50 +08:00
|
|
|
|
2016-04-10 19:01:54 +08:00
|
|
|
static zend_always_inline void zend_unwrap_reference(zval *op) /* {{{ */
|
|
|
|
{
|
|
|
|
if (Z_REFCOUNT_P(op) == 1) {
|
|
|
|
ZVAL_UNREF(op);
|
|
|
|
} else {
|
|
|
|
Z_DELREF_P(op);
|
|
|
|
ZVAL_COPY(op, Z_REFVAL_P(op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
|
2014-09-25 00:31:22 +08:00
|
|
|
END_EXTERN_C()
|
|
|
|
|
1999-04-08 02:10:10 +08:00
|
|
|
#endif
|