by Enrico Zimuel
Principal Software Engineer at Elastic
@ezimuel |
|
17 September 2020
A Just-In-Time (JIT) compiler is a feature of the run-time interpreter, that instead of interpreting bytecode every time a method is invoked, will compile the bytecode into machine code, and then invoke this object code instead
for ($i=0; $i<100; $i++) {
echo $i;
}
Opcode:
L0 (2): ASSIGN CV0($i) int(0)
L1 (2): JMP L4
L2 (3): ECHO CV0($i)
L3 (2): PRE_INC CV0($i)
L4 (2): T1 = IS_SMALLER CV0($i) int(100)
L5 (2): JMPNZ T1 L2
L6 (5): RETURN int(1)
php -d opcache.opt_debug_level=0x20000 -d opcache.enable_cli=1 test.php
sub $0x10, %rsp
lea 0x50(%r14), %rdi
cmp $0xa, 0x8(%rdi)
jnz .L1
mov (%rdi), %rdi
cmp $0x0, 0x18(%rdi)
jnz .L6
add $0x8, %rdi
.L1:
test $0x1, 0x9(%rdi)
jnz .L7
mov $0x0, (%rdi)
mov $0x4, 0x8(%rdi)
.L2:
mov $EG(exception), %rax
cmp $0x0, (%rax)
jnz JIT$$exception_handler
jmp .L4
.L3:
mov $0x7fcc67e2e630, %r15
mov $0x561f82773690, %rax
call *%rax
mov $EG(exception), %rax
cmp $0x0, (%rax)
jnz JIT$$exception_handler
cmp $0x4, 0x58(%r14)
jnz .L9
add $0x1, 0x50(%r14)
.L4:
mov $EG(vm_interrupt), %rax
cmp $0x0, (%rax)
jnz .L11
cmp $0x4, 0x58(%r14)
jnz .L12
cmp $0x64, 0x50(%r14)
jl .L3
.L5:
mov $0x7fcc67e2e6b0, %r15
add $0x10, %rsp
mov $ZEND_RETURN_SPEC_CONST_LABEL, %rax
jmp *%rax
sub $0x10, %rsp
jmp .L4
.L6:
mov $0x7fcc67e2e5c0, %rsi
mov %r15, (%r14)
mov $zend_jit_assign_const_to_typed_ref, %rax
call *%rax
jmp .L2
.L7:
mov (%rdi), %rax
mov %rax, (%rsp)
mov $0x0, (%rdi)
mov $0x4, 0x8(%rdi)
mov (%rsp), %rdi
sub $0x1, (%rdi)
jnz .L8
mov %r15, (%r14)
mov $rc_dtor_func, %rax
call *%rax
jmp .L2
.L8:
mov 0x4(%rdi), %eax
and $0xfffffc10, %eax
cmp $0x10, %eax
jnz .L2
mov $gc_possible_root, %rax
call *%rax
jmp .L2
.L9:
mov %r15, (%r14)
lea 0x50(%r14), %rdi
cmp $0xa, 0x8(%rdi)
jnz .L10
mov (%rdi), %rsi
cmp $0x0, 0x18(%rsi)
lea 0x8(%rsi), %rdi
jz .L10
mov $0x0, %rdx
mov $zend_jit_pre_inc_typed_ref, %rax
call *%rax
mov $EG(exception), %rax
cmp $0x0, (%rax)
jnz JIT$$exception_handler
jmp .L4
.L10:
mov $increment_function, %rax
call *%rax
jmp .L4
.L11:
mov $0x7fcc67e2e670, %r15
jmp JIT$$interrupt_handler
.L12:
cmp $0x5, 0x58(%r14)
jnz .L13
mov $0x64, %rax
vcvtsi2sd %rax, %xmm0, %xmm0
vucomisd 0x50(%r14), %xmm0
ja .L3
jmp .L5
.L13:
mov $0x7fcc67e2e670, %rax
mov %rax, (%r14)
lea 0x50(%r14), %rsi
mov $0x7fcc67e2e5d0, %rdx
lea 0x60(%r14), %rdi
mov $compare_function, %rax
call *%rax
mov $EG(exception), %rax
cmp $0x0, (%rax)
jnz JIT$$exception_handler_undef
cmp $0x0, 0x60(%r14)
jl .L3
jmp .L5
php -d opcache.enable_cli=1 -d opcache.jit=1235
-d opcache.jit_buffer_size=64M -d opcache.jit_debug=1 test.php
Execution of Zend/bench.php (more information)
#[Attribute]
class ListensTo
{
public string $event;
public function __construct(string $event)
{
$this->event = $event;
}
}
class ProductSubscriber
{
#[ListensTo(ProductCreated::class)]
public function onProductCreated(ProductCreated $event) { }
#[ListensTo(ProductDeleted::class)]
public function onProductDeleted(ProductDeleted $event) { }
}
$reflection = new ReflectionClass(ProductSubscriber::class);
foreach ($reflection->getMethods() as $method) {
foreach ($method->getAttributes(ListensTo::class) as $attr) {
$listener = $attr->newInstance();
var_dump($listener);
}
}
// object(ListensTo)#5 (1) {
// ["event"]=>
// string(14) "ProductCreated"
// }
// object(ListensTo)#4 (1) {
// ["event"]=>
// string(14) "ProductDeleted"
// }
class ReflectionAttribute
{
public function getName(): string;
public function getArguments(): array;
public function newInstance(): object;
}
#[Deprecated("use test2() instead")]
function test() { }
test();
// Deprecated: Function test() is deprecated,
// use test2() instead
class Point {
public float $x;
public float $y;
public float $z;
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0,
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
More information: Brent Roose, PHP 8: Constructor property promotion
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
class A {
public function __construct($params = []) {
$this->params = $params;
}
public function create($params): static {
return new static($params);
}
}
class B extends A {
}
$b = new B();
$result = $b->create(['x' => 'y']);
var_dump($result); // object(B) {["params"]=> ["x" => "y"]}
class A {
public function doWhatever(): static {
// Do whatever.
return $this;
}
}
$a = new A();
$result = $a->doWhatever(); // $result === $a
To provide "weakly referenced" map objects
$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);
// object(WeakMap)#1 (1) {
// [0]=> [
// "key" => object(stdClass)#2,
// "value" => int(42)
// ]
// }
// The object is destroyed here, key automatically removed
unset($obj);
var_dump($map);
// object(WeakMap)#1 (0) { }
class Foo {
private WeakMap $cache;
public function getSomethingWithCaching(object $obj) {
return $this->cache[$obj] ??= $this->somethingExpensive($obj);
}
}
??= Null Coalescing Assignment (PHP 7.4+)
interface Stringable
{
public function __toString(): string;
}
Stringable interface is automatically added to classes that implement the __toString() method
Follow me: @ezimuel
Subscribe to Leeds PHP on Meetup
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 Unported License.
I used reveal.js to make this presentation.