On 10 December 2013 12:57, Dmitry Stogov <dmi...@zend.com> wrote:
> You missed the second introduced addition:
>
>
> +                               Z_LVAL_P(result) = Z_LVAL_P(op1) +
> Z_LVAL_P(op2);
>
> So for each $a + $b PHP is going to execute two "add" instructions instead
> of one (plus memory reads and writes).
>

No it is not. Please look carefully at the assembly listing that I
shared below. This is GCC generated code, I am not making this up.

The add is performed only once. The only difference is that the store
register operation which writes the result of the addition back to the
zval is performed conditionally, i.e., only if no overflow occurred.
In fact, the GCC generated code is now closer to the inline assembly
you wrote for x86.

So before my change:

>>     @ LONG(result) = LONG(op1) + LONG(op2)
>>     9160:       f9401ba1        ldr     x1, [x29,#48]
>>     9164:       f9400260        ldr     x0, [x19]
>>     9168:       8b000020        add     x0, x1, x0

Z_LVAL_P(result) = Z_LVAL_P(op1) +  Z_LVAL_P(op2) follows here

>>     916c:       f9000260        str     x0, [x19]
>>
>>     @ conditional branch on overflow
>>     9170:       ca010002        eor     x2, x0, x1
>>     9174:       b6f80262        tbz     x2, #63, 91c0
>> <zif_array_sum+0x118>
>>     9178:       39005274        strb    w20, [x19,#20]
>>


After the change:

>>     9118:       f9400260        ldr     x0, [x19]
>>     911c:       f9401ba1        ldr     x1, [x29,#48]
>>     9120:       ca000023        eor     x3, x1, x0
>>     9124:       8b010002        add     x2, x0, x1
>>
>>     @ conditional branch on overflow
>>     9128:       b6f80583        tbz     x3, #63, 91d8
>> <zif_array_sum+0x130>

the Z_LVAL_P(result) assignment is moved here, after the conditional branch

>>     912c:       f9000262        str     x2, [x19]
>>     9130:       39005274        strb    w20, [x19,#20]
>>

so we have the exact same instruction count, the exact same number of
loads and stores, the only difference is the order.

-- 
Ard.


>>
>> I have trouble understanding why this is unacceptable. The only
>> significant difference is that the str and tbz instructions are
>> switched.
>> Can you elaborate please?
>>
>> --
>> Ard.
>>
>>
>> > It's better to change array_sum() to break aliasing instead of overall
>> > slowdown.
>> > Could you take care about a better fix?
>> >
>> > Thanks. Dmitry.
>> >
>> >
>> > On Tue, Dec 10, 2013 at 3:28 PM, Ard Biesheuvel
>> > <ard.biesheu...@linaro.org>
>> > wrote:
>> >>
>> >> On 10 December 2013 12:25, Dmitry Stogov <dmi...@zend.com> wrote:
>> >> > What exactly are you fixing with this patch?
>> >> > fast_add_function() is used only by VM and opcode operands can't
>> >> > alias
>> >> > (it's
>> >> > guaranteed by compiler).
>> >> > It's also used by array_sum(), but it also can't create aliases
>> >> > between
>> >> > results and operands.
>> >> >
>> >> > What is the reason to slowdown each integer addition?
>> >> >
>> >>
>> >> The patch that was applied to fix
>> >> https://bugs.php.net/bug.php?id=65304 uses calls fast_add_function()
>> >> like this:
>> >>
>> >> fast_add_function(return_value, return_value, &entry_n TSRMLS_CC);
>> >>
>> >> so it does create aliases. If that is undesirable, perhaps we should
>> >> fix array_sum() instead?
>> >>
>> >> However, I think the compiler will be smart enough to factor out the
>> >> op1+op2 operation, so i don't expect any significant slowdown.
>> >>
>> >> --
>> >> Ard.
>> >>
>> >>
>> >>
>> >> > Thanks. Dmitry.
>> >> >
>> >> >
>> >> >
>> >> > On Tue, Dec 10, 2013 at 3:12 PM, Ard Biesheuvel
>> >> > <ardbiesheu...@php.net>
>> >> > wrote:
>> >> >>
>> >> >> Commit:    60d2e70c062e436a6c6cd3c8a17469a083a38b46
>> >> >> Author:    Ard Biesheuvel <ard.biesheu...@linaro.org>         Tue,
>> >> >> 10
>> >> >> Dec
>> >> >> 2013 12:07:46 +0100
>> >> >> Parents:   5a87b7ff39bbf427807c46d1e51e2654259ad394
>> >> >> Branches:  PHP-5.6 master
>> >> >>
>> >> >> Link:
>> >> >>
>> >> >>
>> >> >> http://git.php.net/?p=php-src.git;a=commitdiff;h=60d2e70c062e436a6c6cd3c8a17469a083a38b46
>> >> >>
>> >> >> Log:
>> >> >> Zend: fix overflow handling bug in non-x86 fast_add_function()
>> >> >>
>> >> >> The 'result' argument of fast_add_function() may alias with either
>> >> >> of its operands (or both). Take care not to write to 'result' before
>> >> >> reading op1 and op2.
>> >> >>
>> >> >> Changed paths:
>> >> >>   M  Zend/zend_operators.h
>> >> >>
>> >> >>
>> >> >> Diff:
>> >> >> diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
>> >> >> index 0152e03..5c6fc86 100644
>> >> >> --- a/Zend/zend_operators.h
>> >> >> +++ b/Zend/zend_operators.h
>> >> >> @@ -643,13 +643,18 @@ static zend_always_inline int
>> >> >> fast_add_function(zval
>> >> >> *result, zval *op1, zval *o
>> >> >>                           "n"(ZVAL_OFFSETOF_TYPE)
>> >> >>                         : "rax","cc");
>> >> >>  #else
>> >> >> -                       Z_LVAL_P(result) = Z_LVAL_P(op1) +
>> >> >> Z_LVAL_P(op2);
>> >> >> +                       /*
>> >> >> +                        * '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(result) & LONG_SIGN_MASK))) {
>> >> >> +                               && (Z_LVAL_P(op1) & LONG_SIGN_MASK)
>> >> >> !=
>> >> >> ((Z_LVAL_P(op1) + Z_LVAL_P(op2)) & LONG_SIGN_MASK))) {
>> >> >>                                 Z_DVAL_P(result) = (double)
>> >> >> Z_LVAL_P(op1)
>> >> >> + (double) Z_LVAL_P(op2);
>> >> >>                                 Z_TYPE_P(result) = IS_DOUBLE;
>> >> >>                         } else {
>> >> >> +                               Z_LVAL_P(result) = Z_LVAL_P(op1) +
>> >> >> Z_LVAL_P(op2);
>> >> >>                                 Z_TYPE_P(result) = IS_LONG;
>> >> >>                         }
>> >> >>  #endif
>> >> >>
>> >> >>
>> >> >> --
>> >> >> PHP CVS Mailing List (http://www.php.net/)
>> >> >> To unsubscribe, visit: http://www.php.net/unsub.php
>> >> >>
>> >> >
>> >
>> >
>
>

-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to