php / php-src

The PHP Interpreter
https://www.php.net
Other
38.19k stars 7.75k forks source link

_ir_RSTORE: Assertion `ctx->control' failed #15101

Closed TRowbotham closed 3 months ago

TRowbotham commented 3 months ago

Description

I am hitting an assertion failure in the JIT using PHP 8.4.0alpha2 in a particular project. I have not yet been able to create a small reproducer, so hopefully the following stack trace is useful.

Relevant changed ini settings: opcache.enable_cli=1 opcache.memory_consumption=256 opcache.max_accelerated_files=20000 opcache.jit=1255 opcache.jit_buffer_size=256M

php: /home/trevor/.phpbrew/build/php-8.4.0alpha2/ext/opcache/jit/ir/ir.c:2810: _ir_RSTORE: Assertion `ctx->control' failed.

Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737294845440) at ./nptl/pthread_kill.c:44
44      ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737294845440) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737294845440) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140737294845440, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff73e5476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff73cb7f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff73cb71b in __assert_fail_base (fmt=0x7ffff7580130 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
    assertion=0x7ffff40e7e63 "ctx->control",
    file=0x7ffff40e3ba0 "/home/trevor/.phpbrew/build/php-8.4.0alpha2/ext/opcache/jit/ir/ir.c", line=2810,
    function=<optimized out>) at ./assert/assert.c:92
#6  0x00007ffff73dce96 in __GI___assert_fail (assertion=0x7ffff40e7e63 "ctx->control",
    file=0x7ffff40e3ba0 "/home/trevor/.phpbrew/build/php-8.4.0alpha2/ext/opcache/jit/ir/ir.c", line=2810,
    function=0x7ffff40e8700 <__PRETTY_FUNCTION__.7> "_ir_RSTORE") at ./assert/assert.c:101
#7  0x00007ffff4029220 in _ir_RSTORE (ctx=0x7fffffff1900, reg=15, val=-90)
    at /home/trevor/.phpbrew/build/php-8.4.0alpha2/ext/opcache/jit/ir/ir.c:2810
#8  0x00007ffff3f9416e in jit_STORE_IP (jit=0x7fffffff1900, ref=-90) at ext/opcache/jit/zend_jit_ir.c:905
#9  0x00007ffff3f945b1 in zend_jit_set_ip (jit=0x7fffffff1900, target=0x555536471cb0)
    at ext/opcache/jit/zend_jit_ir.c:1040
#10 0x00007ffff3fce0c2 in zend_jit_trace_handler (jit=0x7fffffff1900, op_array=0x5555364823e8, opline=0x555536471cb0,
    may_throw=1, trace=0x7fffffff71c0) at ext/opcache/jit/zend_jit_ir.c:16410
#11 0x00007ffff4006579 in zend_jit_trace (trace_buffer=0x7fffffff6ef0, parent_trace=655, exit_num=5)
    at ext/opcache/jit/zend_jit_trace.c:6353
#12 0x00007ffff400fc49 in zend_jit_compile_side_trace (trace_buffer=0x7fffffff6ef0, parent_num=655, exit_num=5,
    polymorphism=1) at ext/opcache/jit/zend_jit_trace.c:8192
#13 0x00007ffff4010ae6 in zend_jit_trace_hot_side (execute_data=0x7ffff4418130, parent_num=655, exit_num=5)
    at ext/opcache/jit/zend_jit_trace.c:8413
#14 0x00007ffff40121b7 in zend_jit_trace_exit (exit_num=5, regs=0x7fffffffb040)
    at ext/opcache/jit/zend_jit_trace.c:8682
#15 0x0000555545400620 in ?? ()
#16 0x00005555364822e8 in ?? ()
--Type <RET> for more, q to quit, c to continue without paging--c
#17 0x00005555364a1888 in ?? ()
#18 0x00007ffff3d9dfa0 in ?? ()
#19 0x0000555536bc3a30 in ?? ()
#20 0x00007fffffffb150 in ?? ()
#21 0x00007fffffffb3a0 in ?? ()
#22 0x000055553570c490 in ?? ()
#23 0x00007fffef0f09b0 in ?? ()
#24 0x0000555555dd0365 in zend_abstract_method_call (fbc=0x7fffef0f09b0) at /home/trevor/.phpbrew/build/php-8.4.0alpha2/Zend/zend_object_handlers.c:1640
#25 0x0000000000000000 in ?? ()

PHP Version

PHP 8.4.0alpha2

Operating System

Ubuntu 22.04

dstogov commented 3 months ago

Could you please reproduce this with -d opcache.jit_debug=0x1ff000 and paste information about the last compiled trace (the related PHP code would be also useful).

TRowbotham commented 3 months ago
---- TRACE 640 start (side trace 582/14) Rowbot\DOM\Parser\Collection\ObjectStack::isEmpty() /home/trevor/GitHub/PHPJS/src/Parser/Collection/ObjectStack.php:55
0002 RETURN T0 ; op1(bool)
---- TRACE 640 stop (return)
---- TRACE 640 TSSA start (side trace 582/14) Rowbot\DOM\Parser\Collection\ObjectStack::isEmpty() /home/trevor/GitHub/PHPJS/src/Parser/Collection/ObjectStack.php:55
     ;#0.X0 [!true]
0002 RETURN #0.T0 [!true] ; op1(bool)
---- TRACE 640 TSSA stop (return)
---- TRACE 640 exit info
     exit_0: 0002/0000/2 X1:int
---- TRACE 640 compiled

     TRACE 588 exit 4 Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processToken() /home/trevor/GitHub/PHPJS/src/Parser/HTML/InsertionMode/InitialInsertionMode.php:54
---- TRACE 641 start (side trace 588/4) Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processToken() /home/trevor/GitHub/PHPJS/src/Parser/HTML/InsertionMode/InitialInsertionMode.php:54
0037 INIT_METHOD_CALL 2 THIS string("processDoctypeToken")
     >init Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processDoctypeToken
0038 SEND_VAR CV0($context) 1 ; op1(object of class Rowbot\DOM\Parser\HTML\TreeBuilderContext)
0039 SEND_VAR CV1($token) 2 ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken)
0040 DO_FCALL
     >enter Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processDoctypeToken
0000  CV0($context) = RECV 1
0001  CV1($token) = RECV 2
0002  CV2($publicId) = FETCH_OBJ_R CV1($token) string("publicIdentifier") ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken) val(null)
0003  CV3($systemId) = FETCH_OBJ_R CV1($token) string("systemIdentifier") ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken) val(null)
0004  CV4($name) = FETCH_OBJ_R CV1($token) string("name") ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken) val(string)
0005  T6 = IS_NOT_IDENTICAL CV4($name) string("html") ; op1(string)
0006  ;JMPNZ T6 0011
0007  T6 = TYPE_CHECK TYPE [bool, long, double, string, array, object, resource] CV2($publicId) ; op1(null)
0008  ;JMPNZ T6 0011
0009  T6 = TYPE_CHECK TYPE [bool, long, double, string, array, object, resource] CV3($systemId) ; op1(null)
0010  ;JMPZ T6 0011
0011  V6 = NEW 4 string("Rowbot\\DOM\\DocumentType")
      >init Rowbot\DOM\DocumentType::__construct
0012  CHECK_FUNC_ARG 1
0013  V7 = FETCH_OBJ_FUNC_ARG (ref) CV0($context) string("document") ; op1(object of class Rowbot\DOM\Parser\HTML\TreeBuilderContext) val(object)
0014  SEND_FUNC_ARG V7 1 ; op1(object of class Rowbot\DOM\HTMLDocument)
0015  T7 = COALESCE CV4($name) 0017 ; op1(string)
0017  SEND_VAL_EX T7 2 ; op1(string)
0018  T7 = COALESCE CV2($publicId) 0020 ; op1(null)
0019  T7 = QM_ASSIGN string("")
0020  SEND_VAL_EX T7 3 ; op1(string)
0021  T7 = COALESCE CV3($systemId) 0023 ; op1(null)
0022  T7 = QM_ASSIGN string("")
0023  SEND_VAL_EX T7 4 ; op1(string)
0024  DO_FCALL
      >enter Rowbot\DOM\DocumentType::__construct
0000   CV0($document) = RECV 1
0001   CV1($name) = RECV 2
0002   CV2($publicId) = RECV_INIT 3 string("")
0003   ;CV3($systemId) = RECV_INIT 4 string("")
0004   INIT_STATIC_METHOD_CALL 2 (parent) (exception) CONSTRUCTOR
       >init Rowbot\DOM\Node::__construct
0005   SEND_VAR_EX CV0($document) 1 ; op1(object of class Rowbot\DOM\HTMLDocument)
0006   T4 = FETCH_CLASS_CONSTANT (self) (exception) string("DOCUMENT_TYPE_NODE")
0007   SEND_VAL_EX T4 2 ; op1(int)
0008   DO_FCALL
       >enter Rowbot\DOM\Node::__construct
---- TRACE 641 stop (link to 77)
---- TRACE 641 TSSA start (side trace 588/4) Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processToken() /home/trevor/GitHub/PHPJS/src/Parser/HTML/InsertionMode/InitialInsertionMode.php:54
     ;#0.CV0($context) [rc1, rcn, object]
     ;#1.CV1($token) [rc1, rcn, object]
0037 INIT_METHOD_CALL 2 THIS string("processDoctypeToken")
     >init Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processDoctypeToken
0038 SEND_VAR #0.CV0($context) [rc1, rcn, object] -> #5.CV0($context) NOVAL [rc1, rcn, object] 1 ; op1(object of class Rowbot\DOM\Parser\HTML\TreeBuilderContext)
0039 SEND_VAR #1.CV1($token) [rc1, rcn, object] -> #6.CV1($token) NOVAL [rc1, rcn, object] 2 ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken)
0040 DO_FCALL
     >enter Rowbot\DOM\Parser\HTML\InsertionMode\InitialInsertionMode::processDoctypeToken
      ;#9.CV2($publicId) NOVAL [undef]
      ;#10.CV3($systemId) NOVAL [undef]
      ;#11.CV4($name) NOVAL [undef]
0000  #13.CV0($context) [rc1, rcn, object] = RECV 1
0001  #14.CV1($token) [rc1, rcn, object] = RECV 2
0002  FETCH_OBJ_R #14.CV1($token) [rc1, rcn, object] string("publicIdentifier") #9.CV2($publicId) NOVAL [undef] -> #15.CV2($publicId) [!null] ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken) val(null)
0003  FETCH_OBJ_R #14.CV1($token) [rc1, rcn, object] string("systemIdentifier") #10.CV3($systemId) NOVAL [undef] -> #16.CV3($systemId) [!null] ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken) val(null)
0004  FETCH_OBJ_R #14.CV1($token) [rc1, rcn, object] string("name") #11.CV4($name) NOVAL [undef] -> #17.CV4($name) [!rc1, rcn, string] ; op1(object of class Rowbot\DOM\Parser\Token\DoctypeToken) val(string)
0005  #18.T6 [bool] = IS_NOT_IDENTICAL #17.CV4($name) [!rc1, rcn, string] string("html") ; op1(string)
0006  ;JMPNZ #18.T6 [bool] 0011
0007  #19.T6 [false] = TYPE_CHECK TYPE [bool, long, double, string, array, object, resource] #15.CV2($publicId) [!null] ; op1(null)
0008  ;JMPNZ #19.T6 [false] 0011
0009  #20.T6 [false] = TYPE_CHECK TYPE [bool, long, double, string, array, object, resource] #16.CV3($systemId) [!null] ; op1(null)
0010  ;JMPZ #20.T6 [false] 0011
0011  #21.V6 [rc1, rcn, object] = NEW 4 string("Rowbot\\DOM\\DocumentType")
      >init Rowbot\DOM\DocumentType::__construct
0012  CHECK_FUNC_ARG 1
0013  #22.V7 [!rc1, rcn, object] = FETCH_OBJ_FUNC_ARG (ref) #13.CV0($context) [rc1, rcn, object] string("document") ; op1(object of class Rowbot\DOM\Parser\HTML\TreeBuilderContext) val(object)
0014  SEND_FUNC_ARG #22.V7 [!rc1, rcn, object] 1 ; op1(object of class Rowbot\DOM\HTMLDocument)
0015  #24.T7 [rc1, rcn, string] = COALESCE #17.CV4($name) [!rc1, rcn, string] -> #23.CV4($name) [rc1, rcn, string] 0017 ; op1(string)
0017  SEND_VAL_EX #24.T7 [rc1, rcn, string] 2 ; op1(string)
0018  #26.T7 [] = COALESCE #15.CV2($publicId) [!null] -> #25.CV2($publicId) [null] 0020 ; op1(null)
0019  #27.T7 [rcn, string] = QM_ASSIGN string("")
0020  SEND_VAL_EX #27.T7 [rcn, string] 3 ; op1(string)
0021  #29.T7 [] = COALESCE #16.CV3($systemId) [!null] -> #28.CV3($systemId) [null] 0023 ; op1(null)
0022  #30.T7 [rcn, string] = QM_ASSIGN string("")
0023  SEND_VAL_EX #30.T7 [rcn, string] 4 ; op1(string)
0024  DO_FCALL
      >enter Rowbot\DOM\DocumentType::__construct
       ;#33.CV2($publicId) NOVAL [undef]
       ;#34.CV3($systemId) NOVAL [undef]
0000   #35.CV0($document) [rc1, rcn, object] = RECV 1
0001   #36.CV1($name) [rc1, rcn, string] = RECV 2
0002   RECV_INIT 3 string("") #33.CV2($publicId) NOVAL [undef] -> #37.CV2($publicId) [rc1, rcn, string]
0003   ;RECV_INIT 4 string("") #34.CV3($systemId) NOVAL [undef] -> #38.CV3($systemId) [rc1, rcn, string]
0004   INIT_STATIC_METHOD_CALL 2 (parent) (exception) CONSTRUCTOR
       >init Rowbot\DOM\Node::__construct
0005   SEND_VAR_EX #35.CV0($document) [rc1, rcn, object] -> #39.CV0($document) NOVAL [rc1, rcn, object] 1 ; op1(object of class Rowbot\DOM\HTMLDocument)
0006   #40.T4 [!long] = FETCH_CLASS_CONSTANT (self) (exception) string("DOCUMENT_TYPE_NODE")
0007   SEND_VAL_EX #40.T4 [!long] 2 ; op1(int)
0008   DO_FCALL
       >enter Rowbot\DOM\Node::__construct
---- TRACE 641 TSSA stop (link to 77)
php: /home/trevor/.phpbrew/build/php-8.4.0alpha2/ext/opcache/jit/ir/ir.c:2810: _ir_RSTORE: Assertion `ctx->control' failed.
Aborted

These are the code locations mentioned in the trace.

https://github.com/TRowbotham/PHPDOM/blob/dev/src/Parser/Collection/ObjectStack.php#L55 https://github.com/TRowbotham/PHPDOM/blob/dev/src/Parser/HTML/InsertionMode/InitialInsertionMode.php#L54 https://github.com/TRowbotham/PHPDOM/blob/dev/src/DocumentType.php#L25 https://github.com/TRowbotham/PHPDOM/blob/dev/src/Node.php#L154

I hit this assertion when running vendor/bin/phpunit tests/html5lib/TreeBuilderTest.php

I will also note that this code is using the new property hooks feature, so it may be related to that.

Please let me know if I can provide any additional information.

dstogov commented 3 months ago

Thanks! I've reproduced the failure with a reduced test case.

<?php
class A {
   function test($context, $token) { 
       if ($token instanceof DoctypeToken) {
            $this->processDoctypeToken($context, $token);
        }
    }
    private function processDoctypeToken(TreeBuilderContext $context, DoctypeToken $token): void
    {
        $publicId = $token->publicIdentifier;
        $systemId = $token->systemIdentifier;
        $name = $token->name;

        if ($name !== 'html'
            || $publicId !== null
            || ($systemId !== null && $systemId !== 'about:legacy-compat')) {
        }

        $doctype = new DocumentType($context->document, $name ?? '', $publicId ?? '', $systemId ?? '');
    }
}
class Document {
}
final class TreeBuilderContext {
    public $document;
    public function __construct() {
        $this->document = new Document;
    }
}
abstract class Node {
    public const DOCUMENT_TYPE_NODE = 10;

    protected function __construct(Document $document, int $nodeType)
    {
    }
}
class DocumentType extends Node {
    public readonly string $name;
    public readonly string $publicId;
    public readonly string $systemId;

    public function __construct(
        Document $document,
        string $name,
        string $publicId = '',
        string $systemId = '') {
        parent::__construct($document, self::DOCUMENT_TYPE_NODE);
    }
}
class DoctypeToken {
    public $publicIdentifier;
    public $name;
    public $systemIdentifier;
}

$a = new A;
$doc = new TreeBuilderContext();
$t = new DoctypeToken();
$t->name = "html";
foreach ([$doc, $t] as $token) {
    $a->test($doc, $token);
}
?>