Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions ext/standard/ClassExistsJitHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\standard;

use PHPCompiler\Web\Superglobals;

/**
* class_exists() for compiled JIT/AOT modules (#16185, php-in-PHP).
*
* SSOT: {@see VmReflection::classExists()}
* php-src: ext/standard/basic_functions.c — PHP_FUNCTION(class_exists)
*/
final class ClassExistsJitHelper
{
public static function existsArgv(string $name): bool
{
$ctx = Superglobals::getActiveContext();
if (null === $ctx) {
throw new \LogicException(
'ClassExistsJitHelper::existsArgv() requires an active VM context in this compiler build'
);
}

return VmReflection::classExists(
$ctx,
VmReflection::normalizeGlobalIntrospectionName($name),
true
);
}
}
32 changes: 32 additions & 0 deletions ext/standard/InterfaceExistsJitHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\standard;

use PHPCompiler\Web\Superglobals;

/**
* interface_exists() for compiled JIT/AOT modules (#16185, php-in-PHP).
*
* SSOT: {@see VmReflection::interfaceExists()}
* php-src: ext/standard/basic_functions.c — PHP_FUNCTION(interface_exists)
*/
final class InterfaceExistsJitHelper
{
public static function existsArgv(string $name): bool
{
$ctx = Superglobals::getActiveContext();
if (null === $ctx) {
throw new \LogicException(
'InterfaceExistsJitHelper::existsArgv() requires an active VM context in this compiler build'
);
}

return VmReflection::interfaceExists(
$ctx,
VmReflection::normalizeGlobalIntrospectionName($name),
true
);
}
}
40 changes: 7 additions & 33 deletions ext/standard/JitClassExists.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,20 @@

namespace PHPCompiler\ext\standard;

use PHPCompiler\JIT\Builtin\GlobalIntrospectionNameRuntime;
use PHPCompiler\JIT\Builtin\StringCaseCompare;
use PHPCompiler\JIT\Builtin\StringClassExists;
use PHPCompiler\JIT\Context;
use PHPLLVM\Builder;
use PHPLLVM\Value;

/** LLVM lowering for class_exists() (issues #1214, #1056). */
/**
* JIT/AOT helper for class_exists() via ClassExistsJitHelper PHP (#1214, #16185).
*
* {@see self::stringDataPtr()} remains for other JIT class-name scans.
*/
final class JitClassExists
{
public static function invoke(Context $context, Value $nameStr): Value
{
GlobalIntrospectionNameRuntime::ensureLinked($context);
StringCaseCompare::ensureStrcasecmpLinked($context);
$nameStr = GlobalIntrospectionNameRuntime::normalizeString($context, $nameStr);
$i1 = $context->getTypeFromString('int1');
$i32 = $context->getTypeFromString('int32');
$exists = $i1->constInt(0, false);
$strcasecmpFn = $context->lookupFunction('strcasecmp');
$nameData = self::stringDataPtr($context, $nameStr);

$candidates = $context->type->object->allDeclaredClassLowerNames();
if (null !== $context->runtime->vmContext) {
$candidates = array_values(array_unique(array_merge(
$candidates,
array_keys($context->runtime->vmContext->classes)
)));
}

foreach ($candidates as $lc) {
if (\PHPCompiler\VM\ResourceSupport::isHiddenPseudoClassLc($lc)) {
continue;
}
$candidate = $context->builder->load($context->constantStringFromString($lc));
$candidateData = self::stringDataPtr($context, $candidate);
$cmp = $context->builder->call($strcasecmpFn, $nameData, $candidateData);
$match = $context->builder->icmp(Builder::INT_EQ, $cmp, $i32->constInt(0, false));
$exists = $context->builder->or($exists, $match);
}

return $exists;
return StringClassExists::invoke($context, $nameStr);
}

public static function stringDataPtr(Context $context, Value $strPtr): Value
Expand Down
44 changes: 9 additions & 35 deletions ext/standard/JitInterfaceExists.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

declare(strict_types=1);

/**
* JIT/AOT helper for interface_exists() via InterfaceExistsJitHelper PHP (#1371, #16185).
*/

namespace PHPCompiler\ext\standard;

use PHPCompiler\JIT\Builtin\StringCaseCompare;
use PHPCompiler\JIT\Builtin\StringInterfaceExists;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitStringArg;
use PHPCompiler\JIT\ReflectionBuiltinHelper;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPLLVM\Builder;
use PHPLLVM\Value;

/** LLVM lowering for interface_exists() (issue #1371, #2312). */
final class JitInterfaceExists
{
public static function invoke(Context $context, JITVariable $nameArg): Value
Expand All @@ -22,37 +24,9 @@ public static function invoke(Context $context, JITVariable $nameArg): Value
return ReflectionBuiltinHelper::interfaceExistsLiteral($context, $literal);
}

$nameStr = JitStringArg::lower($context, $nameArg, 'interface_exists() interface name');
StringCaseCompare::ensureStrcasecmpLinked($context);
$i1 = $context->getTypeFromString('int1');
$i32 = $context->getTypeFromString('int32');
$exists = $i1->constInt(0, false);
$strcasecmpFn = $context->lookupFunction('strcasecmp');
$nameData = JitClassExists::stringDataPtr($context, $nameStr);

$candidates = [];
foreach ($context->type->object->allDeclaredClassLowerNames() as $lc) {
if ($context->type->object->isInterfaceClassLc($lc)) {
$candidates[] = $lc;
}
}
if (null !== $context->runtime->vmContext) {
foreach ($context->runtime->vmContext->classes as $lc => $entry) {
if ($entry->isInterface) {
$candidates[] = $lc;
}
}
$candidates = array_values(array_unique($candidates));
}

foreach ($candidates as $lc) {
$candidate = $context->builder->load($context->constantStringFromString($lc));
$candidateData = JitClassExists::stringDataPtr($context, $candidate);
$cmp = $context->builder->call($strcasecmpFn, $nameData, $candidateData);
$match = $context->builder->icmp(Builder::INT_EQ, $cmp, $i32->constInt(0, false));
$exists = $context->builder->or($exists, $match);
}

return $exists;
return StringInterfaceExists::invoke(
$context,
JitStringArg::lower($context, $nameArg, 'interface_exists() interface name')
);
}
}
98 changes: 98 additions & 0 deletions lib/JIT/Builtin/StringClassExists.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\JIT\Builtin;

use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitNestedHelperCoerce;
use PHPCompiler\JIT\JitVmHelperLink;
use PHPLLVM\Value;

/**
* JIT/AOT link for class_exists() via ClassExistsJitHelper PHP (#16185).
*
* Replaces inline strcasecmp LLVM loop in ext/standard/JitClassExists.php.
* SSOT: {@see \PHPCompiler\ext\standard\ClassExistsJitHelper}.
* php-src: ext/standard/basic_functions.c — PHP_FUNCTION(class_exists)
*/
final class StringClassExists
{
private const ABI = '__phpc_jit_class_exists';

private const HELPER_PATH = '/ext/standard/ClassExistsJitHelper.php';

private const INVOKE_HELPER = 'PHPCompiler\\ext\\standard\\ClassExistsJitHelper::existsArgv';

/** @var list<string> */
private const COMPILED_HELPERS = [
self::INVOKE_HELPER,
];

public static function ensureLinked(Context $context): void
{
self::implement($context);
}

public static function ensureStandaloneBodies(Context $context): void
{
self::ensureLinked($context);
}

public static function invoke(Context $context, Value $nameStr): Value
{
self::ensureLinked($context);

return $context->builder->call(
$context->lookupFunction(self::ABI),
$nameStr
);
}

private static function implement(Context $context): void
{
$probe = $context->module->getNamedFunction(self::ABI);
if (null !== $probe && $probe->countBasicBlocks() > 0) {
$context->registerFunction(self::ABI, $probe);

return;
}

$savedBlock = null;
try {
$savedBlock = $context->builder->getInsertBlock();
} catch (\Throwable) {
}

JitVmHelperLink::ensureCompiled($context, self::HELPER_PATH, self::COMPILED_HELPERS, '#16185');

$strPtr = $context->getTypeFromString('__string__*');
$i1 = $context->getTypeFromString('int1');
$fn = null !== $probe
? $probe
: $context->module->addFunction(
self::ABI,
$context->context->functionType($i1, false, $strPtr)
);

$entry = $fn->appendBasicBlock('class_exists_bridge_entry');
$context->builder->positionAtEnd($entry);

$helperFn = JitVmHelperLink::lookupCompiled($context, self::INVOKE_HELPER, '#16185');
$raw = JitNestedHelperCoerce::callHelper(
$context,
$helperFn,
[$fn->getParam(0)]
);
$exists = JitNestedHelperCoerce::coerceHelperScalarResult($context, $raw, $i1);
$context->builder->returnValue($exists);

$context->registerFunction(self::ABI, $fn);

if (null !== $savedBlock) {
$context->builder->positionAtEnd($savedBlock);
} else {
$context->builder->clearInsertionPosition();
}
}
}
98 changes: 98 additions & 0 deletions lib/JIT/Builtin/StringInterfaceExists.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\JIT\Builtin;

use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitNestedHelperCoerce;
use PHPCompiler\JIT\JitVmHelperLink;
use PHPLLVM\Value;

/**
* JIT/AOT link for interface_exists() via InterfaceExistsJitHelper PHP (#16185).
*
* Replaces inline strcasecmp LLVM loop in ext/standard/JitInterfaceExists.php.
* SSOT: {@see \PHPCompiler\ext\standard\InterfaceExistsJitHelper}.
* php-src: ext/standard/basic_functions.c — PHP_FUNCTION(interface_exists)
*/
final class StringInterfaceExists
{
private const ABI = '__phpc_jit_interface_exists';

private const HELPER_PATH = '/ext/standard/InterfaceExistsJitHelper.php';

private const INVOKE_HELPER = 'PHPCompiler\\ext\\standard\\InterfaceExistsJitHelper::existsArgv';

/** @var list<string> */
private const COMPILED_HELPERS = [
self::INVOKE_HELPER,
];

public static function ensureLinked(Context $context): void
{
self::implement($context);
}

public static function ensureStandaloneBodies(Context $context): void
{
self::ensureLinked($context);
}

public static function invoke(Context $context, Value $nameStr): Value
{
self::ensureLinked($context);

return $context->builder->call(
$context->lookupFunction(self::ABI),
$nameStr
);
}

private static function implement(Context $context): void
{
$probe = $context->module->getNamedFunction(self::ABI);
if (null !== $probe && $probe->countBasicBlocks() > 0) {
$context->registerFunction(self::ABI, $probe);

return;
}

$savedBlock = null;
try {
$savedBlock = $context->builder->getInsertBlock();
} catch (\Throwable) {
}

JitVmHelperLink::ensureCompiled($context, self::HELPER_PATH, self::COMPILED_HELPERS, '#16185');

$strPtr = $context->getTypeFromString('__string__*');
$i1 = $context->getTypeFromString('int1');
$fn = null !== $probe
? $probe
: $context->module->addFunction(
self::ABI,
$context->context->functionType($i1, false, $strPtr)
);

$entry = $fn->appendBasicBlock('interface_exists_bridge_entry');
$context->builder->positionAtEnd($entry);

$helperFn = JitVmHelperLink::lookupCompiled($context, self::INVOKE_HELPER, '#16185');
$raw = JitNestedHelperCoerce::callHelper(
$context,
$helperFn,
[$fn->getParam(0)]
);
$exists = JitNestedHelperCoerce::coerceHelperScalarResult($context, $raw, $i1);
$context->builder->returnValue($exists);

$context->registerFunction(self::ABI, $fn);

if (null !== $savedBlock) {
$context->builder->positionAtEnd($savedBlock);
} else {
$context->builder->clearInsertionPosition();
}
}
}
Loading
Loading