[php-src] PR #23940: JIT: Make overflow add/sub cheaper
| From: | ndossche | Date: | Sat, 26 Sep 2026 23:51:03 +0000 |
| Subject: | [php-src] PR #23940: JIT: Make overflow add/sub cheaper | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-38851@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/23940
Author: ndossche
PHP code:
```php
<?php
function sum($n) {
$s = 0;
for ($i = 0; $i < $n; $i++) {
$s += $i;
}
return $s;
}
var_dump(sum(10000000));
```
Old x86-64 code:
```asm
TRACE-1$sum$4:
subq $0x30, %rsp
movabsq $EG(jit_trace_num), %rax
movl $1, (%rax)
cmpb $4, 0x58(%r14)
jne jit$$trace_exit_0
cmpb $4, 0x68(%r14)
jne jit$$trace_exit_1
cmpb $4, 0x78(%r14)
jne jit$$trace_exit_2
movq 0x50(%r14), %rax
movq 0x60(%r14), %rcx
movq 0x70(%r14), %rdx
.L1:
cmpq %rax, %rdx
jge jit$$trace_exit_3
movq %rdx, %rsi
addq %rcx, %rsi
jo jit$$trace_exit_4
leaq 1(%rdx), %rdx
movabsq $EG(vm_interrupt), %rcx
movb (%rcx), %cl
testb %cl, %cl
jne jit$$trace_exit_5
movq %rsi, %rcx
jmp .L1
```
Note that this code uses an extra register and 2 extra moves (for %rcx) to carry the result. This is
because the trace exit wants the old result. But we can avoid the extra register and extra moves if
we simply reconstruct the old value upon exit.
This patch implements that idea, which results in the following new code:
```asm
TRACE-1$sum$4:
subq $0x30, %rsp
movabsq $EG(jit_trace_num), %rax
movl $1, (%rax)
cmpb $4, 0x58(%r14)
jne jit$$trace_exit_0
cmpb $4, 0x68(%r14)
jne jit$$trace_exit_1
cmpb $4, 0x78(%r14)
jne jit$$trace_exit_2
movq 0x50(%r14), %rax
movq 0x60(%r14), %rcx
movq 0x70(%r14), %rdx
jmp .L2
; ... nops for alignment
.L1:
leaq 1(%rdx), %rdx
movabsq $EG(vm_interrupt), %rsi
movb (%rsi), %sil
testb %sil, %sil
jne jit$$trace_exit_5
.L2:
cmpq %rax, %rdx
jge jit$$trace_exit_3
addq %rdx, %rcx
jno .L1
subq %rdx, %rcx
jmp jit$$trace_exit_4
```
Notice how the addition now no longer needs extra moves and how the result is kept in %rcx all the
time. The cold path under
jno restores the old value right before jumping to the trace
exit.