Consolidate userland functions to rely on a single implementation
Split get_comparison functions into a reverse and a normal one
Use bool type when applicable
Packed arrays are likely common in this case, as with array_shift which
already has a similar optimization.
For the following benchmark:
```php
<?php
for ($i = 0; $i < 10000000; $i++) {
$a = [0, 1, 2, 3, 4, 5];
array_unshift($a, -3, -2, -1);
}
```
On an i7-4790:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 753.8 ms ± 23.8 ms [User: 749.8 ms, System: 2.1 ms]
Range (min … max): 734.3 ms … 818.6 ms 10 runs
Benchmark 2: ./sapi/cli/php_old x.php
Time (mean ± σ): 972.5 ms ± 5.0 ms [User: 968.8 ms, System: 1.4 ms]
Range (min … max): 967.8 ms … 984.3 ms 10 runs
Summary
./sapi/cli/php x.php ran
1.29 ± 0.04 times faster than ./sapi/cli/php_old x.php
```
We never need to use refcounted copies for arguments because the copy to
the call frame already increments the refcount.
For the following benchmark:
```php
$a = range(0, 10);
for ($i = 0; $i < 1000000; $i++)
array_walk($a, fn ($val, $idx, $arg) => $val + $arg, 2);
```
On an i7-4790:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 593.0 ms ± 5.4 ms [User: 589.8 ms, System: 1.7 ms]
Range (min … max): 583.3 ms … 600.9 ms 10 runs
Benchmark 2: ./sapi/cli/php_old x.php
Time (mean ± σ): 637.8 ms ± 4.6 ms [User: 633.9 ms, System: 2.2 ms]
Range (min … max): 633.4 ms … 649.2 ms 10 runs
Summary
./sapi/cli/php x.php ran
1.08 ± 0.01 times faster than ./sapi/cli/php_old x.php
```
On an i7-1185G7:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 362.3 ms ± 2.0 ms [User: 359.9 ms, System: 1.9 ms]
Range (min … max): 359.6 ms … 367.1 ms 10 runs
Benchmark 2: ./sapi/cli/php_old x.php
Time (mean ± σ): 385.5 ms ± 1.8 ms [User: 383.2 ms, System: 1.9 ms]
Range (min … max): 381.5 ms … 387.2 ms 10 runs
Summary
./sapi/cli/php x.php ran
1.06 ± 0.01 times faster than ./sapi/cli/php_old x.php
```
Move the refcount update outside of the loop.
For the following benchmark:
```php
$r = range(0, 1000);
$v = new stdClass();
for ($i = 0; $i < 100000; $i++) {
array_fill_keys($r, $v);
}
```
On an i7-4790:
```
Benchmark 1: ./sapi/cli/php_old ../x.php
Time (mean ± σ): 507.5 ms ± 4.8 ms [User: 505.1 ms, System: 1.2 ms]
Range (min … max): 501.2 ms … 518.4 ms 10 runs
Benchmark 2: ./sapi/cli/php ../x.php
Time (mean ± σ): 479.8 ms ± 3.1 ms [User: 476.8 ms, System: 1.8 ms]
Range (min … max): 475.0 ms … 486.7 ms 10 runs
Summary
./sapi/cli/php ../x.php ran
1.06 ± 0.01 times faster than ./sapi/cli/php_old ../x.php
```
On an i7-1185G7:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 343.9 ms ± 3.1 ms [User: 341.1 ms, System: 2.3 ms]
Range (min … max): 337.9 ms … 347.8 ms 10 runs
Benchmark 2: ./sapi/cli/php_old x.php
Time (mean ± σ): 357.8 ms ± 2.3 ms [User: 355.7 ms, System: 1.6 ms]
Range (min … max): 355.0 ms … 362.6 ms 10 runs
Summary
./sapi/cli/php x.php ran
1.04 ± 0.01 times faster than ./sapi/cli/php_old x.php
```
The reason this happens is because the array_unique operation happens in-place
because the input array is RC1.
At one point during comparison an exception is thrown which will capture the
arguments in the backtrace, which will increment the refcount of the RC1 array
to 2. Then a modification happens after the throw on the RC2 array causing the
assertion failure.
We shouldn't try continue work after an exception happened during the sort.
Closes GH-20059.
There are 2 issues:
1. When a MULTISORT_ABORT happens, it frees func, but func may point to
ARRAYG(multisort_func), which would be a problem with nested
invocations as it can destroy that of the "parent" invocation.
To solve this, delay assigning to the globals.
2. The old globals were not restored which means that nested invocations
with different flags will cause a wrong sorting function to be used.
Closes GH-19319.
For this benchmark:
```php
$length = 25;
for ($i=0;$i<1000;$i++)
array_chunk(range(0, 10000), $length);
```
On an i7-4790, length=25 speeds up by 1.8x and length=1 by 1.27x.
On an i7-1185G7, length=25 speeds up by 1.08x and length=1 by 1.02x.
These cause cache misses due to global access, in phpstan
(notably the array_map).
Initializing these isn't necessary because ZPP initializes it for us.
Only for optional arguments do we need to be careful; for `array_filter`
we still reset the `fci` but not `fci_cache` because `fci` is not
necessarily set by ZPP but is conditionally used to access `fci_cache`.