Skip to content

Instantly share code, notes, and snippets.

@staabm
Created August 16, 2026 08:19
Show Gist options
  • Select an option

  • Save staabm/127abcf5aafc47d0b178eda39e525ad2 to your computer and use it in GitHub Desktop.

Select an option

Save staabm/127abcf5aafc47d0b178eda39e525ad2 to your computer and use it in GitHub Desktop.
return_type_check_elision_repro.php
<?php
declare(strict_types=1);
/*
* Reproducer for commit 95d3ed1af81930f06b8e72b0bf414e52b1353a31:
* "Elide provably-passing return type checks at compile time"
*
* Run this script on a build before and after that commit, ideally with:
* ./sapi/cli/php -n -dopcache.enable_cli=0 return_type_check_elision_repro.php
*
* The "direct" cases should get faster after the commit because
* ZEND_VERIFY_RETURN_TYPE is not emitted for provably safe returns.
*/
if (PHP_SAPI !== 'cli') {
fwrite(STDERR, "Run from CLI.\n");
exit(1);
}
final class GetterBench
{
private int $x = 7;
public function direct(): int
{
return $this->x;
}
public function control(): int
{
$tmp = $this->x;
return $tmp;
}
}
function cmp_direct(int $a, int $b): bool
{
return $a < $b;
}
function cmp_control(int $a, int $b): bool
{
$tmp = $a < $b;
return $tmp;
}
function run_getter_direct(int $iterations): int
{
$obj = new GetterBench();
$sum = 0;
for ($i = 0; $i < $iterations; $i++) {
$sum += $obj->direct(); $sum += $obj->direct(); $sum += $obj->direct(); $sum += $obj->direct();
$sum += $obj->direct(); $sum += $obj->direct(); $sum += $obj->direct(); $sum += $obj->direct();
}
return $sum;
}
function run_getter_control(int $iterations): int
{
$obj = new GetterBench();
$sum = 0;
for ($i = 0; $i < $iterations; $i++) {
$sum += $obj->control(); $sum += $obj->control(); $sum += $obj->control(); $sum += $obj->control();
$sum += $obj->control(); $sum += $obj->control(); $sum += $obj->control(); $sum += $obj->control();
}
return $sum;
}
function run_cmp_direct(int $iterations): int
{
$sum = 0;
$end = $iterations + 3;
for ($i = 0; $i < $iterations; $i++) {
$sum += cmp_direct($i, $end) ? 1 : 0; $sum += cmp_direct($i + 1, $end) ? 1 : 0;
$sum += cmp_direct($i + 2, $end) ? 1 : 0; $sum += cmp_direct($i + 3, $end) ? 1 : 0;
$sum += cmp_direct($i + 4, $end) ? 1 : 0; $sum += cmp_direct($i + 5, $end) ? 1 : 0;
$sum += cmp_direct($i + 6, $end) ? 1 : 0; $sum += cmp_direct($i + 7, $end) ? 1 : 0;
}
return $sum;
}
function run_cmp_control(int $iterations): int
{
$sum = 0;
$end = $iterations + 3;
for ($i = 0; $i < $iterations; $i++) {
$sum += cmp_control($i, $end) ? 1 : 0; $sum += cmp_control($i + 1, $end) ? 1 : 0;
$sum += cmp_control($i + 2, $end) ? 1 : 0; $sum += cmp_control($i + 3, $end) ? 1 : 0;
$sum += cmp_control($i + 4, $end) ? 1 : 0; $sum += cmp_control($i + 5, $end) ? 1 : 0;
$sum += cmp_control($i + 6, $end) ? 1 : 0; $sum += cmp_control($i + 7, $end) ? 1 : 0;
}
return $sum;
}
function run_case(string $label, int $iterations, int $rounds, callable $fn): array
{
$times = [];
$sink = 0;
for ($w = 0; $w < 2; $w++) {
$sink ^= $fn($iterations);
}
for ($r = 0; $r < $rounds; $r++) {
$start = hrtime(true);
$sink ^= $fn($iterations);
$times[] = (hrtime(true) - $start) / 1e9;
}
sort($times);
return [
'label' => $label,
'best' => $times[0],
'avg' => array_sum($times) / count($times),
'sink' => $sink,
];
}
$iterations = isset($argv[1]) ? max(1, (int) $argv[1]) : 5_000_000;
$rounds = isset($argv[2]) ? max(1, (int) $argv[2]) : 7;
gc_disable();
$cases = [
run_case('getter-direct', $iterations, $rounds, 'run_getter_direct'),
run_case('getter-control', $iterations, $rounds, 'run_getter_control'),
run_case('cmp-direct', $iterations, $rounds, 'run_cmp_direct'),
run_case('cmp-control', $iterations, $rounds, 'run_cmp_control'),
];
$byLabel = [];
foreach ($cases as $row) {
$byLabel[$row['label']] = $row;
}
printf(
"PHP %s\niterations=%d rounds=%d calls_per_round=%d\n\n",
PHP_VERSION,
$iterations,
$rounds,
$iterations * 8
);
echo str_pad('case', 16) . str_pad('best(s)', 12) . str_pad('avg(s)', 12) . "sink\n";
echo str_repeat('-', 50) . "\n";
foreach ($cases as $row) {
printf(
"%-16s%-12.6f%-12.6f%d\n",
$row['label'],
$row['best'],
$row['avg'],
$row['sink']
);
}
echo "\nDirect/control ratio inside each pair (best time, lower is better):\n";
printf(
"%-16s %.4fx\n",
'getter',
$byLabel['getter-direct']['best'] / $byLabel['getter-control']['best']
);
printf(
"%-16s %.4fx\n",
'cmp',
$byLabel['cmp-direct']['best'] / $byLabel['cmp-control']['best']
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment