From 674468f8d344032bc8aa94dd35611b1bb8a8b3ee Mon Sep 17 00:00:00 2001 From: PurHur Date: Thu, 9 Jul 2026 21:10:34 +0000 Subject: [PATCH] php-in-php: route array_find family closure JIT through ArrayFindJitHelper PHP (#17547) Delegate closure/arrow callbacks from ArrayFindHelper LLVM loops to ArrayFindRuntime + walkWithClosure nested PHP helper (JitVmHelperLink), matching ArrayMap/ArrayReduce closure paths. Removes dead closure branch from resolvePredicateHandler. Co-authored-by: Cursor --- docs/bootstrap-inventory.md | 837 ++++++++++++----------- docs/bootstrap-profile.json | 5 +- ext/standard/ArrayFindJitHelper.php | 76 ++ lib/JIT/ArrayFindHelper.php | 7 +- lib/JIT/Builtin/ArrayFindRuntime.php | 75 +- test/unit/ArrayFindRuntimeShrinkTest.php | 14 +- 6 files changed, 584 insertions(+), 430 deletions(-) diff --git a/docs/bootstrap-inventory.md b/docs/bootstrap-inventory.md index f1fdd44950..c9374029fc 100644 --- a/docs/bootstrap-inventory.md +++ b/docs/bootstrap-inventory.md @@ -8,11 +8,11 @@ Regenerate: `php script/bootstrap-inventory.php` | Metric | Count | |--------|------:| -| PHP files on vm.php path | 4308 | -| Phase A inventory files (M2 ratio SSOT) | 4308 | +| PHP files on vm.php path | 4309 | +| Phase A inventory files (M2 ratio SSOT) | 4309 | | Phase A ratio-deferred paths | 0 | | Source constructs flagged (blockers) | 0 | -| Source constructs flagged (warnings) | 11606 | +| Source constructs flagged (warnings) | 11616 | ## Compiler CFG gaps (`lib/Compiler.php`) @@ -137,6 +137,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/dom/DocumentConstruct.php` | 0 | 1 | | `ext/dom/DocumentCreateAttribute.php` | 0 | 1 | | `ext/dom/DocumentCreateAttributeNS.php` | 0 | 1 | +| `ext/dom/DocumentCreateCDATASection.php` | 0 | 1 | | `ext/dom/DocumentCreateComment.php` | 0 | 1 | | `ext/dom/DocumentCreateDocumentFragment.php` | 0 | 1 | | `ext/dom/DocumentCreateElement.php` | 0 | 1 | @@ -247,7 +248,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/dom/TokenListReplace.php` | 0 | 1 | | `ext/dom/TokenListSupports.php` | 0 | 1 | | `ext/dom/TokenListToggle.php` | 0 | 1 | -| `ext/dom/VmDom.php` | 0 | 285 | +| `ext/dom/VmDom.php` | 0 | 290 | | `ext/dom/VmDomInstanceInvoke.php` | 0 | 2 | | `ext/dom/VmDomJitDispatch.php` | 0 | 9 | | `ext/dom/VmDomJitFrame.php` | 0 | 1 | @@ -694,7 +695,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/standard/ArrayFillJitHelper.php` | 0 | 3 | | `ext/standard/ArrayFillKeysJitHelper.php` | 0 | 1 | | `ext/standard/ArrayFilterJitHelper.php` | 0 | 2 | -| `ext/standard/ArrayFindJitHelper.php` | 0 | 4 | +| `ext/standard/ArrayFindJitHelper.php` | 0 | 7 | | `ext/standard/ArrayFlipJitHelper.php` | 0 | 3 | | `ext/standard/ArrayIntersectAssocJitHelper.php` | 0 | 1 | | `ext/standard/ArrayIntersectJitHelper.php` | 0 | 1 | @@ -2763,7 +2764,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `lib/Cli/PhpcInit.php` | 0 | 1 | | `lib/Cli/PhpcRun.php` | 0 | 1 | | `lib/CloneWithSyntaxRejector.php` | 0 | 2 | -| `lib/Compiler.php` | 0 | 429 | +| `lib/Compiler.php` | 0 | 430 | | `lib/Compiler/AbstractMethodVisibilityCheck.php` | 0 | 3 | | `lib/Compiler/AsymmetricVisibilityCompileCheck.php` | 0 | 4 | | `lib/Compiler/AttributeClassRegistry.php` | 0 | 1 | @@ -4956,6 +4957,11 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag **Warnings** (review for bootstrap subset): - 1 class method(s) +### `ext/dom/DocumentCreateCDATASection.php` + +**Warnings** (review for bootstrap subset): +- 1 class method(s) + ### `ext/dom/DocumentCreateComment.php` **Warnings** (review for bootstrap subset): @@ -5520,14 +5526,12 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/dom/VmDom.php` **Warnings** (review for bootstrap subset): -- new Variable (line 155) -- new Variable (line 156) - new Variable (line 157) - new Variable (line 158) - new Variable (line 159) -- new ClassEntry (line 162) -- new ClassProperty (line 164) -- new ClassProperty (line 165) +- new Variable (line 160) +- new Variable (line 161) +- new ClassEntry (line 164) - new ClassProperty (line 166) - new ClassProperty (line 167) - new ClassProperty (line 168) @@ -5537,273 +5541,280 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new ClassProperty (line 172) - new ClassProperty (line 173) - new ClassProperty (line 174) +- new ClassProperty (line 175) - new ClassProperty (line 176) -- new NodeCloneNode (line 178) -- new NodeReplaceChild (line 180) -- new NodeInsertBefore (line 182) -- new NodeRemoveChild (line 184) -- new NodeIsSameNode (line 186) -- new NodeIsEqualNode (line 189) -- new NodeHasChildNodes (line 192) -- new NodeContains (line 195) -- new NodeGetRootNode (line 199) -- new NodeAppend (line 202) -- new NodeReplaceChildren (line 205) -- new NodePrepend (line 208) -- new NodeBefore (line 210) -- new NodeAfter (line 212) -- new NodeReplaceWith (line 214) -- new NodeRemove (line 216) -- new NodeLookupPrefix (line 218) -- new NodeLookupNamespaceURI (line 220) -- new NodeGetLineNo (line 222) -- new NodeGetNodePath (line 224) -- new NodeHasAttributes (line 226) -- new NodeIsDefaultNamespace (line 228) -- new NodeIsSupported (line 230) -- new NodeCompareDocumentPosition (line 233) -- new NodeNormalize (line 244) -- new NodeC14N (line 246) -- new NodeC14NFile (line 249) -- new ClassEntry (line 254) -- new ClassProperty (line 257) -- new TextSplitText (line 258) -- new ClassEntry (line 263) -- new ClassProperty (line 266) -- new CharacterDataAppendData (line 267) -- new CharacterDataDeleteData (line 270) -- new CharacterDataInsertData (line 273) -- new CharacterDataReplaceData (line 276) -- new CharacterDataSubstringData (line 279) -- new ClassEntry (line 284) -- new ClassProperty (line 287) -- new ClassEntry (line 290) -- new ClassProperty (line 293) -- new ClassEntry (line 296) -- new ClassProperty (line 299) -- new ClassProperty (line 300) +- new ClassProperty (line 178) +- new NodeCloneNode (line 180) +- new NodeReplaceChild (line 182) +- new NodeInsertBefore (line 184) +- new NodeRemoveChild (line 186) +- new NodeIsSameNode (line 188) +- new NodeIsEqualNode (line 191) +- new NodeHasChildNodes (line 194) +- new NodeContains (line 197) +- new NodeGetRootNode (line 201) +- new NodeAppend (line 204) +- new NodeReplaceChildren (line 207) +- new NodePrepend (line 210) +- new NodeBefore (line 212) +- new NodeAfter (line 214) +- new NodeReplaceWith (line 216) +- new NodeRemove (line 218) +- new NodeLookupPrefix (line 220) +- new NodeLookupNamespaceURI (line 222) +- new NodeGetLineNo (line 224) +- new NodeGetNodePath (line 226) +- new NodeHasAttributes (line 228) +- new NodeIsDefaultNamespace (line 230) +- new NodeIsSupported (line 232) +- new NodeCompareDocumentPosition (line 235) +- new NodeNormalize (line 246) +- new NodeC14N (line 248) +- new NodeC14NFile (line 251) +- new ClassEntry (line 256) +- new ClassProperty (line 259) +- new TextSplitText (line 260) +- new ClassEntry (line 265) +- new ClassProperty (line 268) +- new ClassEntry (line 271) +- new ClassProperty (line 274) +- new CharacterDataAppendData (line 275) +- new CharacterDataDeleteData (line 278) +- new CharacterDataInsertData (line 281) +- new CharacterDataReplaceData (line 284) +- new CharacterDataSubstringData (line 287) +- new ClassEntry (line 292) +- new ClassProperty (line 295) +- new ClassEntry (line 298) - new ClassProperty (line 301) -- new ClassProperty (line 302) -- new ClassEntry (line 305) -- new ClassProperty (line 314) -- new NodeListItem (line 315) -- new NodeListCount (line 317) -- new NodeListRewind (line 319) -- new NodeListValid (line 321) -- new NodeListCurrent (line 323) -- new NodeListKey (line 325) -- new NodeListNext (line 327) -- new ClassEntry (line 331) -- new ClassProperty (line 340) -- new NamedNodeMapItem (line 341) -- new NamedNodeMapGetNamedItem (line 343) -- new NamedNodeMapGetNamedItemNS (line 346) -- new NamedNodeMapCount (line 349) -- new NamedNodeMapRewind (line 351) -- new NamedNodeMapValid (line 353) -- new NamedNodeMapCurrent (line 355) -- new NamedNodeMapKey (line 357) -- new NamedNodeMapNext (line 359) -- new ClassEntry (line 364) -- new ClassProperty (line 367) -- new ClassProperty (line 368) -- new TokenListAdd (line 369) -- new TokenListRemove (line 371) -- new TokenListContains (line 373) -- new TokenListToggle (line 375) -- new TokenListItem (line 377) -- new TokenListReplace (line 379) -- new TokenListSupports (line 381) -- new TokenListCount (line 383) -- new ClassEntry (line 388) -- new XPathConstruct (line 390) -- new XPathQuery (line 394) -- new XPathEvaluate (line 396) -- new XPathRegisterNamespace (line 398) -- new ClassEntry (line 403) -- new ImplementationCreateDocument (line 405) -- new ImplementationCreateDocumentType (line 407) -- new ImplementationGetFeature (line 409) -- new ImplementationHasFeature (line 411) -- new ClassEntry (line 416) -- new ClassProperty (line 419) -- new ClassProperty (line 420) -- new ClassProperty (line 421) -- new ClassProperty (line 422) -- new ClassEntry (line 425) +- new ClassEntry (line 304) +- new ClassProperty (line 307) +- new ClassProperty (line 308) +- new ClassProperty (line 309) +- new ClassProperty (line 310) +- new ClassEntry (line 313) +- new ClassProperty (line 322) +- new NodeListItem (line 323) +- new NodeListCount (line 325) +- new NodeListRewind (line 327) +- new NodeListValid (line 329) +- new NodeListCurrent (line 331) +- new NodeListKey (line 333) +- new NodeListNext (line 335) +- new ClassEntry (line 339) +- new ClassProperty (line 348) +- new NamedNodeMapItem (line 349) +- new NamedNodeMapGetNamedItem (line 351) +- new NamedNodeMapGetNamedItemNS (line 354) +- new NamedNodeMapCount (line 357) +- new NamedNodeMapRewind (line 359) +- new NamedNodeMapValid (line 361) +- new NamedNodeMapCurrent (line 363) +- new NamedNodeMapKey (line 365) +- new NamedNodeMapNext (line 367) +- new ClassEntry (line 372) +- new ClassProperty (line 375) +- new ClassProperty (line 376) +- new TokenListAdd (line 377) +- new TokenListRemove (line 379) +- new TokenListContains (line 381) +- new TokenListToggle (line 383) +- new TokenListItem (line 385) +- new TokenListReplace (line 387) +- new TokenListSupports (line 389) +- new TokenListCount (line 391) +- new ClassEntry (line 396) +- new XPathConstruct (line 398) +- new XPathQuery (line 402) +- new XPathEvaluate (line 404) +- new XPathRegisterNamespace (line 406) +- new ClassEntry (line 411) +- new ImplementationCreateDocument (line 413) +- new ImplementationCreateDocumentType (line 415) +- new ImplementationGetFeature (line 417) +- new ImplementationHasFeature (line 419) +- new ClassEntry (line 424) +- new ClassProperty (line 427) - new ClassProperty (line 428) - new ClassProperty (line 429) - new ClassProperty (line 430) -- new ClassProperty (line 431) -- new ClassEntry (line 434) -- new DocumentConstruct (line 437) -- new ClassProperty (line 441) -- new ClassProperty (line 442) -- new ClassProperty (line 443) -- new ClassProperty (line 444) -- new ClassProperty (line 445) -- new ClassProperty (line 446) -- new ClassProperty (line 447) -- new ClassProperty (line 448) +- new ClassEntry (line 433) +- new ClassProperty (line 436) +- new ClassProperty (line 437) +- new ClassProperty (line 438) +- new ClassProperty (line 439) +- new ClassEntry (line 442) +- new DocumentConstruct (line 445) - new ClassProperty (line 449) - new ClassProperty (line 450) - new ClassProperty (line 451) -- new DocumentLoadXML (line 452) -- new DocumentLoad (line 454) -- new DocumentLoadHTML (line 456) -- new DocumentCreateElement (line 459) -- new DocumentCreateElementNS (line 461) -- new DocumentCreateAttributeNS (line 463) -- new DocumentCreateAttribute (line 465) -- new DocumentCreateDocumentFragment (line 467) -- new DocumentCreateEntityReference (line 469) -- new DocumentCreateTextNode (line 472) -- new DocumentCreateComment (line 475) -- new DocumentAppendChild (line 478) -- new DocumentSaveXML (line 480) -- new DocumentSaveHTML (line 482) -- new DocumentSaveHTMLFile (line 485) -- new DocumentGetElementsByTagName (line 488) -- new DocumentGetElementsByTagNameNS (line 490) -- new DocumentGetElementById (line 493) -- new DocumentImportNode (line 495) -- new DocumentAdoptNode (line 497) -- new DocumentRegisterNodeClass (line 500) -- new DocumentNormalizeDocument (line 503) -- new DocumentXInclude (line 506) -- new DocumentSchemaValidate (line 508) -- new DocumentRelaxNGValidate (line 511) -- new ClassEntry (line 516) -- new ClassProperty (line 519) -- new ClassProperty (line 520) -- new ElementAppendChild (line 521) -- new ElementGetAttribute (line 523) -- new ElementGetAttributeNode (line 525) -- new ElementGetAttributeNS (line 527) -- new ElementHasAttribute (line 529) -- new ElementHasAttributeNS (line 531) -- new ElementRemoveAttribute (line 533) -- new ElementRemoveAttributeNode (line 535) -- new ElementSetAttribute (line 537) -- new ElementSetAttributeNode (line 539) -- new ElementSetAttributeNS (line 541) -- new ElementRemoveAttributeNS (line 543) -- new ElementSetIdAttribute (line 545) -- new ElementSetIdAttributeNS (line 548) -- new ElementGetElementsByTagName (line 551) -- new ElementGetElementsByTagNameNS (line 553) -- new ElementGetAttributeNames (line 557) -- new ElementInsertAdjacentHTML (line 562) -- new ElementInsertAdjacentElement (line 567) -- new ElementInsertAdjacentText (line 572) -- new ElementToggleAttribute (line 577) -- new ElementGetInnerHTML (line 582) -- new ElementGetOuterHTML (line 585) -- new ClassProperty (line 590) -- new ClassEntry (line 594) -- new ClassProperty (line 597) -- new FragmentAppendChild (line 598) -- new FragmentAppendXML (line 600) -- new ObjectEntry (line 614) -- new DomNodeState (line 616) -- new Variable (line 624) -- new ObjectEntry (line 647) -- new DomNodeState (line 652) -- new Variable (line 677) -- new ObjectEntry (line 705) -- new DomNodeState (line 726) -- new DomNodeState (line 776) -- new ObjectEntry (line 793) -- new DomNodeState (line 798) -- new Variable (line 814) -- new ObjectEntry (line 830) -- new DomNodeState (line 835) -- new Variable (line 852) -- new ObjectEntry (line 867) -- new DomNodeState (line 875) -- new Variable (line 887) -- new DOMException (line 904) -- new Variable (line 908) -- new Variable (line 914) -- new DOMException (line 923) -- new Variable (line 952) -- new DOMException (line 965) -- new DOMException (line 978) -- new Variable (line 990) -- new DOMException (line 1056) -- new DOMException (line 1108) -- new DOMException (line 1121) -- new DOMException (line 1144) -- new DOMException (line 1158) -- new DOMException (line 1184) -- new DOMException (line 1239) -- new DOMException (line 1266) -- new DOMException (line 1270) -- new DOMException (line 1283) -- new DOMException (line 1287) -- new DOMException (line 1297) -- new ObjectEntry (line 1668) -- new DomNodeState (line 1673) -- new ObjectEntry (line 1689) -- new DomNodeState (line 1694) -- new DOMException (line 1733) -- new DOMException (line 1743) -- new DOMException (line 1756) +- new ClassProperty (line 452) +- new ClassProperty (line 453) +- new ClassProperty (line 454) +- new ClassProperty (line 455) +- new ClassProperty (line 456) +- new ClassProperty (line 457) +- new ClassProperty (line 458) +- new ClassProperty (line 459) +- new DocumentLoadXML (line 460) +- new DocumentLoad (line 462) +- new DocumentLoadHTML (line 464) +- new DocumentCreateElement (line 467) +- new DocumentCreateElementNS (line 469) +- new DocumentCreateAttributeNS (line 471) +- new DocumentCreateAttribute (line 473) +- new DocumentCreateDocumentFragment (line 475) +- new DocumentCreateEntityReference (line 477) +- new DocumentCreateTextNode (line 480) +- new DocumentCreateComment (line 483) +- new DocumentCreateCDATASection (line 486) +- new DocumentAppendChild (line 489) +- new DocumentSaveXML (line 491) +- new DocumentSaveHTML (line 493) +- new DocumentSaveHTMLFile (line 496) +- new DocumentGetElementsByTagName (line 499) +- new DocumentGetElementsByTagNameNS (line 501) +- new DocumentGetElementById (line 504) +- new DocumentImportNode (line 506) +- new DocumentAdoptNode (line 508) +- new DocumentRegisterNodeClass (line 511) +- new DocumentNormalizeDocument (line 514) +- new DocumentXInclude (line 517) +- new DocumentSchemaValidate (line 519) +- new DocumentRelaxNGValidate (line 522) +- new ClassEntry (line 527) +- new ClassProperty (line 530) +- new ClassProperty (line 531) +- new ElementAppendChild (line 532) +- new ElementGetAttribute (line 534) +- new ElementGetAttributeNode (line 536) +- new ElementGetAttributeNS (line 538) +- new ElementHasAttribute (line 540) +- new ElementHasAttributeNS (line 542) +- new ElementRemoveAttribute (line 544) +- new ElementRemoveAttributeNode (line 546) +- new ElementSetAttribute (line 548) +- new ElementSetAttributeNode (line 550) +- new ElementSetAttributeNS (line 552) +- new ElementRemoveAttributeNS (line 554) +- new ElementSetIdAttribute (line 556) +- new ElementSetIdAttributeNS (line 559) +- new ElementGetElementsByTagName (line 562) +- new ElementGetElementsByTagNameNS (line 564) +- new ElementGetAttributeNames (line 568) +- new ElementInsertAdjacentHTML (line 573) +- new ElementInsertAdjacentElement (line 578) +- new ElementInsertAdjacentText (line 583) +- new ElementToggleAttribute (line 588) +- new ElementGetInnerHTML (line 593) +- new ElementGetOuterHTML (line 596) +- new ClassProperty (line 601) +- new ClassEntry (line 605) +- new ClassProperty (line 608) +- new FragmentAppendChild (line 609) +- new FragmentAppendXML (line 611) +- new ObjectEntry (line 625) +- new DomNodeState (line 627) +- new Variable (line 635) +- new ObjectEntry (line 658) +- new DomNodeState (line 663) +- new Variable (line 688) +- new ObjectEntry (line 716) +- new DomNodeState (line 737) +- new DomNodeState (line 787) +- new ObjectEntry (line 804) +- new DomNodeState (line 809) +- new Variable (line 825) +- new ObjectEntry (line 841) +- new DomNodeState (line 846) +- new Variable (line 863) +- new ObjectEntry (line 878) +- new DomNodeState (line 886) +- new Variable (line 898) +- new DOMException (line 915) +- new Variable (line 919) +- new Variable (line 925) +- new DOMException (line 934) +- new Variable (line 963) +- new DOMException (line 976) +- new DOMException (line 989) +- new Variable (line 1001) +- new DOMException (line 1067) +- new DOMException (line 1119) +- new DOMException (line 1132) +- new DOMException (line 1155) +- new DOMException (line 1169) +- new DOMException (line 1195) +- new DOMException (line 1250) +- new DOMException (line 1277) +- new DOMException (line 1281) +- new DOMException (line 1294) +- new DOMException (line 1298) +- new DOMException (line 1308) +- new ObjectEntry (line 1679) +- new DomNodeState (line 1684) +- new ObjectEntry (line 1700) +- new DomNodeState (line 1705) +- new ObjectEntry (line 1721) +- new DomNodeState (line 1726) +- new DOMException (line 1765) - new DOMException (line 1775) -- new DOMException (line 1789) -- new ObjectEntry (line 1815) -- new DomNodeState (line 1820) -- new Variable (line 1829) -- new ObjectEntry (line 1839) -- new DomNodeState (line 1844) -- new Variable (line 1849) -- new ObjectEntry (line 2323) -- new DomNodeState (line 2325) -- new DOMException (line 2472) -- new DOMException (line 2498) -- new Variable (line 2512) -- new DOMException (line 2526) -- new DOMException (line 2529) -- new DOMException (line 2537) -- new DOMException (line 2563) -- new DOMException (line 2576) -- new DOMException (line 2762) -- new DOMException (line 2959) -- new DOMException (line 2963) -- new DOMException (line 3052) -- new DOMException (line 3056) -- new DOMException (line 3114) -- new DOMException (line 3131) -- new DOMException (line 3160) -- new DOMException (line 3201) -- new DOMException (line 3226) -- new DOMException (line 3700) -- new Variable (line 3724) -- new DOMException (line 3904) -- new DOMException (line 3985) -- new DOMException (line 4027) -- new ObjectEntry (line 4112) -- new DomNodeState (line 4116) -- new Variable (line 4123) -- new ObjectEntry (line 4323) -- new DomNodeState (line 4327) -- new Variable (line 4334) -- new ObjectEntry (line 4354) -- new DomNodeState (line 4357) -- new Variable (line 4365) -- new DOMException (line 4567) -- new DOMException (line 4574) -- new DOMException (line 4581) -- new DOMException (line 4585) -- new DOMException (line 4588) -- new DOMException (line 4597) -- new Variable (line 5290) -- new Variable (line 5303) -- new DOMException (line 5381) -- new DOMException (line 5392) -- new DOMException (line 5458) -- new DOMException (line 5466) -- 269 class method(s) +- new DOMException (line 1788) +- new DOMException (line 1807) +- new DOMException (line 1821) +- new ObjectEntry (line 1847) +- new DomNodeState (line 1852) +- new Variable (line 1861) +- new ObjectEntry (line 1871) +- new DomNodeState (line 1876) +- new Variable (line 1881) +- new ObjectEntry (line 2355) +- new DomNodeState (line 2357) +- new DOMException (line 2504) +- new DOMException (line 2530) +- new Variable (line 2544) +- new DOMException (line 2558) +- new DOMException (line 2561) +- new DOMException (line 2569) +- new DOMException (line 2595) +- new DOMException (line 2608) +- new DOMException (line 2794) +- new DOMException (line 2991) +- new DOMException (line 2995) +- new DOMException (line 3084) +- new DOMException (line 3088) +- new DOMException (line 3146) +- new DOMException (line 3163) +- new DOMException (line 3192) +- new DOMException (line 3233) +- new DOMException (line 3258) +- new DOMException (line 3732) +- new Variable (line 3756) +- new DOMException (line 3979) +- new DOMException (line 4060) +- new DOMException (line 4102) +- new ObjectEntry (line 4187) +- new DomNodeState (line 4191) +- new Variable (line 4198) +- new ObjectEntry (line 4398) +- new DomNodeState (line 4402) +- new Variable (line 4409) +- new ObjectEntry (line 4429) +- new DomNodeState (line 4432) +- new Variable (line 4440) +- new DOMException (line 4642) +- new DOMException (line 4649) +- new DOMException (line 4656) +- new DOMException (line 4660) +- new DOMException (line 4663) +- new DOMException (line 4672) +- new Variable (line 5378) +- new Variable (line 5391) +- new DOMException (line 5473) +- new DOMException (line 5484) +- new DOMException (line 5550) +- new DOMException (line 5558) +- 272 class method(s) - 2 closure(s) ### `ext/dom/VmDomInstanceInvoke.php` @@ -9129,10 +9140,13 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/standard/ArrayFindJitHelper.php` **Warnings** (review for bootstrap subset): -- new Variable (line 34) -- new Variable (line 36) -- new Variable (line 38) -- 1 class method(s) +- new Variable (line 35) +- new Variable (line 37) +- new Variable (line 39) +- new Variable (line 93) +- new Variable (line 95) +- new Variable (line 97) +- 3 class method(s) ### `ext/standard/ArrayFlipJitHelper.php` @@ -21665,7 +21679,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `ext/xml/VmXml.php` **Warnings** (review for bootstrap subset): -- 14 class method(s) +- 16 class method(s) ### `ext/xml/xml_get_error_code.php` @@ -22452,133 +22466,134 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new Variable (line 13121) - new Operand\Temporary (line 13123) - new OpCode (line 13170) -- new Temporary (line 15944) -- new Operand\Temporary (line 28951) -- new Operand\Temporary (line 28956) -- new Operand\Temporary (line 29029) -- new Operand\Temporary (line 29034) -- new OpCode (line 29171) -- new OpCode (line 29272) -- new OpCode (line 29361) -- new OpCode (line 29442) -- new OpCode (line 29527) -- new OpCode (line 29617) -- new OpCode (line 29721) -- new CfgVariable (line 32024) -- new Literal (line 32024) -- new Variable (line 32056) -- new Variable (line 32110) -- new OpCode (line 32139) -- new Operand\Temporary (line 32901) -- new OpCode (line 32903) -- new OpCode (line 32931) -- new OpCode (line 32947) -- new OpCode (line 32955) -- new OpCode (line 32962) -- new OpCode (line 32974) -- new OpCode (line 32991) -- new OpCode (line 32997) -- new OpCode (line 33003) -- new OpCode (line 33008) -- new OpCode (line 33024) -- new OpCode (line 33037) -- new Variable (line 33048) -- new Operand\Literal (line 33050) -- new OpCode (line 33053) -- new OpCode (line 33123) -- new OpCode (line 33134) -- new OpCode (line 33147) -- new OpCode (line 33321) -- new OpCode (line 33586) -- new OpCode (line 33592) -- new OpCode (line 33598) -- new OpCode (line 33623) -- new Variable (line 33650) -- new Temporary (line 33652) -- new Variable (line 33660) -- new Temporary (line 33662) -- new OpCode (line 33710) -- new Operand\Temporary (line 33731) -- new Operand\Temporary (line 33737) -- new Operand\Temporary (line 33743) -- new Operand\Temporary (line 33749) -- new Operand\Temporary (line 33758) -- new Operand\Temporary (line 35529) -- new Variable (line 35930) -- new Variable (line 35934) -- new Variable (line 35942) -- new Variable (line 35946) -- new Variable (line 36230) -- new OpCode (line 36305) -- new OpCode (line 36353) -- new OpCode (line 36370) -- new OpCode (line 36389) -- new OpCode (line 36409) -- new OpCode (line 36451) -- new OpCode (line 36486) -- new OpCode (line 36504) -- new OpCode (line 36537) -- new OpCode (line 36578) -- new OpCode (line 36594) -- new OpCode (line 36612) -- new OpCode (line 36629) -- new OpCode (line 36643) -- new OpCode (line 36665) -- new OpCode (line 36687) -- new OpCode (line 36696) -- new OpCode (line 36715) -- new OpCode (line 36792) -- new OpCode (line 36805) -- new OpCode (line 36818) -- new OpCode (line 36823) -- new OpCode (line 36840) -- new OpCode (line 36886) -- new OpCode (line 36925) -- new OpCode (line 37017) -- new OpCode (line 37056) -- new OpCode (line 37134) -- new OpCode (line 37173) -- new OpCode (line 37224) -- new Operand\Temporary (line 38885) -- new Operand\Temporary (line 39377) +- new Temporary (line 15952) +- new Operand\Temporary (line 29068) +- new Operand\Temporary (line 29073) +- new Operand\Temporary (line 29146) +- new Operand\Temporary (line 29151) +- new OpCode (line 29288) +- new OpCode (line 29389) +- new OpCode (line 29478) +- new OpCode (line 29559) +- new OpCode (line 29644) +- new OpCode (line 29734) +- new OpCode (line 29838) +- new CfgVariable (line 32151) +- new Literal (line 32151) +- new Variable (line 32183) +- new Variable (line 32237) +- new OpCode (line 32266) +- new Operand\Temporary (line 33028) +- new OpCode (line 33030) +- new OpCode (line 33058) +- new OpCode (line 33074) +- new OpCode (line 33082) +- new OpCode (line 33089) +- new OpCode (line 33101) +- new OpCode (line 33118) +- new OpCode (line 33124) +- new OpCode (line 33130) +- new OpCode (line 33135) +- new OpCode (line 33151) +- new OpCode (line 33164) +- new Variable (line 33175) +- new Operand\Literal (line 33177) +- new OpCode (line 33180) +- new OpCode (line 33250) +- new OpCode (line 33261) +- new OpCode (line 33274) +- new OpCode (line 33448) +- new OpCode (line 33713) +- new OpCode (line 33719) +- new OpCode (line 33725) +- new OpCode (line 33750) +- new Variable (line 33777) +- new Temporary (line 33779) +- new Variable (line 33787) +- new Temporary (line 33789) +- new OpCode (line 33837) +- new Operand\Temporary (line 33858) +- new Operand\Temporary (line 33864) +- new Operand\Temporary (line 33870) +- new Operand\Temporary (line 33876) +- new Operand\Temporary (line 33885) +- new Operand\Temporary (line 35656) +- new Variable (line 36057) +- new Variable (line 36061) +- new Variable (line 36069) +- new Variable (line 36073) +- new Variable (line 36357) +- new OpCode (line 36428) +- new OpCode (line 36459) +- new OpCode (line 36507) +- new OpCode (line 36524) +- new OpCode (line 36543) +- new OpCode (line 36563) +- new OpCode (line 36605) +- new OpCode (line 36640) +- new OpCode (line 36658) +- new OpCode (line 36691) +- new OpCode (line 36732) +- new OpCode (line 36748) +- new OpCode (line 36766) +- new OpCode (line 36783) +- new OpCode (line 36797) +- new OpCode (line 36819) +- new OpCode (line 36841) +- new OpCode (line 36850) +- new OpCode (line 36869) +- new OpCode (line 36946) +- new OpCode (line 36959) +- new OpCode (line 36972) +- new OpCode (line 36977) +- new OpCode (line 36994) +- new OpCode (line 37040) +- new OpCode (line 37079) +- new OpCode (line 37171) +- new OpCode (line 37210) +- new OpCode (line 37288) +- new OpCode (line 37328) +- new OpCode (line 37390) +- new Operand\Temporary (line 39070) - new Operand\Temporary (line 39563) -- new Operand\Temporary (line 39595) -- new Operand\Temporary (line 39691) -- new Operand\Temporary (line 39813) -- new OpCode (line 39815) -- new OpCode (line 41193) -- new Operand\Temporary (line 41651) -- new OpCode (line 42175) -- new Operand\Literal (line 42474) -- new Variable (line 42476) -- new OpCode (line 42522) -- new OpCode (line 42737) -- new OpCode (line 42747) -- new OpCode (line 42750) -- new Operand\Temporary (line 42777) -- new Operand\Temporary (line 42814) -- new OpCode (line 42816) -- new OpCode (line 42827) -- new Operand\Temporary (line 42830) -- new OpCode (line 42832) -- new OpCode (line 42840) -- new OpCode (line 42848) -- new OpCode (line 42856) -- new OpCode (line 42859) -- new OpCode (line 42887) -- new OpCode (line 42910) -- new OpCode (line 42927) -- new OpCode (line 42934) -- new Operand\Literal (line 43613) -- new OpCode (line 44708) -- new Literal (line 44740) -- new OpCode (line 44839) -- new Variable (line 44846) -- new Temporary (line 44848) -- new OpCode (line 44851) -- new CompileFatal (line 45186) -- new CompileFatal (line 45441) -- 956 class method(s) +- new Operand\Temporary (line 39749) +- new Operand\Temporary (line 39781) +- new Operand\Temporary (line 39877) +- new Operand\Temporary (line 39999) +- new OpCode (line 40001) +- new OpCode (line 41379) +- new Operand\Temporary (line 41837) +- new OpCode (line 42361) +- new Operand\Literal (line 42660) +- new Variable (line 42662) +- new OpCode (line 42708) +- new OpCode (line 42923) +- new OpCode (line 42933) +- new OpCode (line 42936) +- new Operand\Temporary (line 42963) +- new Operand\Temporary (line 43000) +- new OpCode (line 43002) +- new OpCode (line 43013) +- new Operand\Temporary (line 43016) +- new OpCode (line 43018) +- new OpCode (line 43026) +- new OpCode (line 43034) +- new OpCode (line 43042) +- new OpCode (line 43045) +- new OpCode (line 43073) +- new OpCode (line 43096) +- new OpCode (line 43113) +- new OpCode (line 43120) +- new Operand\Literal (line 43799) +- new OpCode (line 44920) +- new Literal (line 44952) +- new OpCode (line 45051) +- new Variable (line 45058) +- new Temporary (line 45060) +- new OpCode (line 45063) +- new CompileFatal (line 45398) +- new CompileFatal (line 45653) +- 959 class method(s) - 47 closure(s) ### `lib/Compiler/AbstractMethodVisibilityCheck.php` @@ -23271,11 +23286,11 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `lib/JIT/ArrayFindHelper.php` **Warnings** (review for bootstrap subset): -- new Variable (line 209) -- new Variable (line 211) -- new Variable (line 459) -- new Variable (line 670) -- new Variable (line 683) +- new Variable (line 213) +- new Variable (line 215) +- new Variable (line 463) +- new Variable (line 671) +- new Variable (line 684) - 28 class method(s) ### `lib/JIT/ArrayMapCallbackPolicy.php` @@ -23406,7 +23421,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag ### `lib/JIT/Builtin/ArrayFindRuntime.php` **Warnings** (review for bootstrap subset): -- 6 class method(s) +- 8 class method(s) ### `lib/JIT/Builtin/ArrayFlipRuntime.php` @@ -27231,11 +27246,11 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag **Warnings** (review for bootstrap subset): - new VmVariable (line 106) - new VmVariable (line 169) -- new VmVariable (line 200) -- new VmVariable (line 206) -- new VmVariable (line 211) -- new VmVariable (line 217) -- 7 class method(s) +- new VmVariable (line 213) +- new VmVariable (line 219) +- new VmVariable (line 224) +- new VmVariable (line 230) +- 8 class method(s) ### `lib/JIT/CallUnpackHelper.php` diff --git a/docs/bootstrap-profile.json b/docs/bootstrap-profile.json index 2b7304c451..e25ba963a0 100644 --- a/docs/bootstrap-profile.json +++ b/docs/bootstrap-profile.json @@ -141,6 +141,7 @@ "ext/dom/DocumentConstruct.php", "ext/dom/DocumentCreateAttribute.php", "ext/dom/DocumentCreateAttributeNS.php", + "ext/dom/DocumentCreateCDATASection.php", "ext/dom/DocumentCreateComment.php", "ext/dom/DocumentCreateDocumentFragment.php", "ext/dom/DocumentCreateElement.php", @@ -4542,9 +4543,9 @@ "test/bootstrap-aot/lib_opcode/main.php" ], "totals": { - "inventory_files": 4308, + "inventory_files": 4309, "excluded": 0, - "eligible": 4308, + "eligible": 4309, "aot_lint_targets": 113, "aot_link_targets": 100, "aot_link_lib_targets": 1 diff --git a/ext/standard/ArrayFindJitHelper.php b/ext/standard/ArrayFindJitHelper.php index 079355ad21..6cabc1cbeb 100644 --- a/ext/standard/ArrayFindJitHelper.php +++ b/ext/standard/ArrayFindJitHelper.php @@ -6,6 +6,7 @@ use PHPCompiler\VM\HashTable; use PHPCompiler\VM\Variable; +use PHPCompiler\Web\Superglobals; /** * array_find family string-builtin path for compiled JIT/AOT modules (#14842, php-in-PHP). @@ -77,4 +78,79 @@ public static function walkWithBuiltin(HashTable $ht, string $builtinName, int $ return $out; } + + public static function walkWithClosure(HashTable $ht, Variable $closure, int $mode, bool $strict): Variable + { + $ctx = Superglobals::getActiveContext(); + if (null === $ctx) { + throw new \LogicException( + 'ArrayFindJitHelper::walkWithClosure() requires an active VM context in this compiler build' + ); + } + $closureState = VmClosureCall::resolve($closure); + $function = self::functionNameForMode($mode); + $keyFirst = VmArrayValueCallback::callbackKeyFirst($function); + $out = new Variable(); + foreach ($ht->iterateKeyed(true) as [$key, $value]) { + $item = new Variable(); + $item->copyFrom($value); + $keyVar = new Variable(); + $keyVar->copyFrom($key); + $result = VmClosureCall::invoke( + $ctx, + $closureState, + $keyFirst ? $keyVar : $item, + $keyFirst ? $item : $keyVar, + ); + $matches = VmArrayValueCallback::predicateMatches($result, $strict); + if (self::MODE_ANY === $mode || self::MODE_ANY_KEY === $mode) { + if ($matches) { + $out->bool(true); + + return $out; + } + + continue; + } + if (self::MODE_ALL === $mode || self::MODE_ALL_KEY === $mode) { + if (!$matches) { + $out->bool(false); + + return $out; + } + + continue; + } + if ($matches) { + if (self::MODE_FIND_KEY === $mode) { + $out->copyFrom($key); + } else { + $out->copyFrom($value); + } + + return $out; + } + } + if (self::MODE_ANY === $mode || self::MODE_ANY_KEY === $mode) { + $out->bool(false); + } elseif (self::MODE_ALL === $mode || self::MODE_ALL_KEY === $mode) { + $out->bool(true); + } else { + $out->null(); + } + + return $out; + } + + private static function functionNameForMode(int $mode): string + { + return match ($mode) { + self::MODE_FIND_KEY => 'array_find_key', + self::MODE_ANY => 'array_any', + self::MODE_ALL => 'array_all', + self::MODE_ALL_KEY => 'array_all_key', + self::MODE_ANY_KEY => 'array_any_key', + default => 'array_find', + }; + } } diff --git a/lib/JIT/ArrayFindHelper.php b/lib/JIT/ArrayFindHelper.php index 32ddfe0a68..fb9bb27bc4 100644 --- a/lib/JIT/ArrayFindHelper.php +++ b/lib/JIT/ArrayFindHelper.php @@ -8,6 +8,7 @@ use PHPCompiler\ext\standard\JitArrayElem; use PHPCompiler\ext\standard\VmInternalCall; use PHPCompiler\Func\Internal; +use PHPCompiler\JIT\Builtin\ArrayFindRuntime; use PHPCompiler\JIT\Builtin\TypeErrorRaise; use PHPCompiler\VM\Variable as VmVariable; use PHPLLVM\BasicBlock; @@ -124,6 +125,9 @@ private static function buildFromArray( bool $unaryInternalUsesKey = false, ): Value { $strictI1 ??= $context->constantFromBool(false); + if (ArrayFindCallbackPolicy::isClosureJitLowerable($callback)) { + return ArrayFindRuntime::walkClosure($context, $array, $callback, $mode, $strictI1); + } $keyFirst = self::callbackKeyFirstForMode($mode, $unaryInternalUsesKey); JitArrayElem::requireArrayArg($context, $array, self::functionNameForMode($mode)); if (self::MODE_ANY === $mode || self::MODE_ALL === $mode) { @@ -525,9 +529,6 @@ private static function finishResult( /** @return array{0: string, 1: Call|Internal} */ private static function resolvePredicateHandler(Context $context, Variable $callback): array { - if (null !== $callback->closureCall) { - return ['closure', $callback->closureCall]; - } $name = $callback->compileTimeString; if (null !== $name) { try { diff --git a/lib/JIT/Builtin/ArrayFindRuntime.php b/lib/JIT/Builtin/ArrayFindRuntime.php index ecee7d40b1..5a1a8908f0 100644 --- a/lib/JIT/Builtin/ArrayFindRuntime.php +++ b/lib/JIT/Builtin/ArrayFindRuntime.php @@ -8,14 +8,16 @@ use PHPCompiler\JIT\ArrayFindCallbackPolicy; use PHPCompiler\JIT\ArrayMapCallbackPolicy; use PHPCompiler\JIT\Context; +use PHPCompiler\JIT\JitValueBox; use PHPCompiler\JIT\JitVmHelperLink; use PHPCompiler\JIT\Variable as JITVariable; use PHPLLVM\Value; /** - * JIT/AOT link for array_find family string-builtin path via ArrayFindJitHelper PHP (#14842). + * JIT/AOT link for array_find family via ArrayFindJitHelper PHP (#14842, #17547). * - * Closure and compile-unit user-function callbacks still use {@see \PHPCompiler\JIT\ArrayFindHelper} LLVM. + * String-builtin and closure callbacks route through nested PHP helpers; compile-unit + * user-function callbacks still use {@see \PHPCompiler\JIT\ArrayFindHelper} LLVM. * SSOT: {@see \PHPCompiler\ext\standard\array_find} and siblings. * php-src: ext/standard/array.c */ @@ -23,13 +25,18 @@ final class ArrayFindRuntime { private const ABI_FIND = '__array_find__builtin'; + private const ABI_FIND_CLOSURE = '__array_find__closure'; + private const HELPER_PATH = '/ext/standard/ArrayFindJitHelper.php'; private const WALK_HELPER = 'PHPCompiler\\ext\\standard\\ArrayFindJitHelper::walkWithBuiltin'; + private const WALK_CLOSURE_HELPER = 'PHPCompiler\\ext\\standard\\ArrayFindJitHelper::walkWithClosure'; + /** @var list */ private const COMPILED_HELPERS = [ self::WALK_HELPER, + self::WALK_CLOSURE_HELPER, ]; public static function walk( @@ -61,6 +68,33 @@ public static function walk( ); } + public static function walkClosure( + Context $context, + JITVariable $array, + JITVariable $callback, + int $mode, + Value $strictI1 + ): Value { + if ($callback->isNullConstant) { + throw new \TypeError(ArrayFindCallbackPolicy::invalidCallbackTypeError('array_find')); + } + if (!ArrayFindCallbackPolicy::isClosureJitLowerable($callback)) { + throw new \LogicException(ArrayFindCallbackPolicy::jitRejectionMessage()); + } + + self::ensureLinked($context); + $ht = ArrayBuiltinHelper::loadHashTable($context, $array); + $modeVal = $context->constantFromInteger($mode, 'int64'); + + return $context->builder->call( + $context->lookupFunction(self::ABI_FIND_CLOSURE), + $ht, + JitValueBox::valuePtrFromVariable($context, $callback), + $modeVal, + $strictI1 + ); + } + public static function ensureLinked(Context $context): void { self::implement($context); @@ -73,8 +107,7 @@ public static function ensureStandaloneBodies(Context $context): void public static function implement(Context $context): void { - $probe = $context->module->getNamedFunction(self::ABI_FIND); - if (null !== $probe && $probe->countBasicBlocks() > 0) { + if (self::bridgesComplete($context)) { self::registerLinkedRuntime($context); return; @@ -89,6 +122,7 @@ public static function implement(Context $context): void $htPtr = $context->getTypeFromString('__hashtable__*'); $strPtr = $context->getTypeFromString('__string__*'); $i64 = $context->getTypeFromString('int64'); + $i1 = $context->getTypeFromString('int1'); $valuePtr = $context->getTypeFromString('__value__*'); JitVmHelperLink::ensureBridge( $context, @@ -101,6 +135,17 @@ public static function implement(Context $context): void self::COMPILED_HELPERS, '#14842' ); + JitVmHelperLink::ensureBridge( + $context, + self::ABI_FIND_CLOSURE, + 'array_find_closure_bridge_entry', + [$htPtr, $valuePtr, $i64, $i1], + $valuePtr, + self::WALK_CLOSURE_HELPER, + self::HELPER_PATH, + self::COMPILED_HELPERS, + '#17547' + ); self::registerLinkedRuntime($context); if (null !== $savedBlock) { @@ -119,12 +164,26 @@ private static function isStringBuiltinCallback(JITVariable $callback): bool ); } + private static function bridgesComplete(Context $context): bool + { + foreach ([self::ABI_FIND, self::ABI_FIND_CLOSURE] as $name) { + $probe = $context->module->getNamedFunction($name); + if (null === $probe || 0 === $probe->countBasicBlocks()) { + return false; + } + } + + return true; + } + private static function registerLinkedRuntime(Context $context): void { - $fn = $context->module->getNamedFunction(self::ABI_FIND); - if (null === $fn || 0 === $fn->countBasicBlocks()) { - throw new \LogicException(self::ABI_FIND.' missing after ArrayFindRuntime bridge (#14842)'); + foreach ([self::ABI_FIND, self::ABI_FIND_CLOSURE] as $name) { + $fn = $context->module->getNamedFunction($name); + if (null === $fn || 0 === $fn->countBasicBlocks()) { + throw new \LogicException($name.' missing after ArrayFindRuntime bridge (#17547)'); + } + $context->registerFunction($name, $fn); } - $context->registerFunction(self::ABI_FIND, $fn); } } diff --git a/test/unit/ArrayFindRuntimeShrinkTest.php b/test/unit/ArrayFindRuntimeShrinkTest.php index 88de9963b4..2f288db7e7 100644 --- a/test/unit/ArrayFindRuntimeShrinkTest.php +++ b/test/unit/ArrayFindRuntimeShrinkTest.php @@ -6,19 +6,21 @@ use PHPUnit\Framework\TestCase; -/** array_find family string-builtin JIT uses LLVM ArrayFindHelper until nested PHP walk is AOT-safe (#4009). */ +/** array_find family closure JIT routes through ArrayFindJitHelper PHP (#17547). */ final class ArrayFindRuntimeShrinkTest extends TestCase { - public function testArrayFindStringBuiltinUsesLlvmNotNestedPhpWalk(): void + public function testArrayFindClosureUsesNestedPhpWalk(): void { $helper = (string) file_get_contents(__DIR__.'/../../lib/JIT/ArrayFindHelper.php'); - $this->assertStringNotContainsString('ArrayFindRuntime::walk', $helper); + $this->assertStringContainsString('ArrayFindRuntime::walkClosure', $helper); $this->assertStringContainsString('resolvePredicateHandler', $helper); - $this->assertStringContainsString('TypeErrorRaise::emitBranchOrAbortOnValueErrorFailure', $helper); - $this->assertStringNotContainsString('lookupFunction(\'abort\')', $helper); $runtime = (string) file_get_contents(__DIR__.'/../../lib/JIT/Builtin/ArrayFindRuntime.php'); - $this->assertStringContainsString('ArrayFindJitHelper', $runtime); + $this->assertStringContainsString('walkWithClosure', $runtime); $this->assertStringNotContainsString('LOAD_TYPE_STANDALONE', $runtime); + + $jitHelper = (string) file_get_contents(__DIR__.'/../../ext/standard/ArrayFindJitHelper.php'); + $this->assertStringContainsString('walkWithClosure', $jitHelper); + $this->assertStringContainsString('VmClosureCall::invoke', $jitHelper); } }