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
47 changes: 23 additions & 24 deletions docs/bootstrap-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Regenerate: `php script/bootstrap-inventory.php`
| Phase A inventory files (M2 ratio SSOT) | 7381 |
| Phase A ratio-deferred paths | 0 |
| Source constructs flagged (blockers) | 0 |
| Source constructs flagged (warnings) | 23918 |
| Source constructs flagged (warnings) | 23917 |

## Compiler CFG gaps (`lib/Compiler.php`)

Expand Down Expand Up @@ -4493,7 +4493,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
| `ext/standard/stream_context_get_options.php` | 0 | 1 |
| `ext/standard/stream_context_get_params.php` | 0 | 1 |
| `ext/standard/stream_context_set_default.php` | 0 | 1 |
| `ext/standard/stream_context_set_option.php` | 0 | 3 |
| `ext/standard/stream_context_set_option.php` | 0 | 2 |
| `ext/standard/stream_context_set_options.php` | 0 | 2 |
| `ext/standard/stream_context_set_params.php` | 0 | 2 |
| `ext/standard/stream_copy_to_stream.php` | 0 | 1 |
Expand Down Expand Up @@ -29637,7 +29637,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `ext/standard/JitFdiv.php`

**Warnings** (review for bootstrap subset):
- 15 class method(s)
- 17 class method(s)

### `ext/standard/JitFeof.php`

Expand Down Expand Up @@ -31404,7 +31404,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `ext/standard/JitStreamContextSetOption.php`

**Warnings** (review for bootstrap subset):
- 4 class method(s)
- 15 class method(s)

### `ext/standard/JitStreamContextSetOptions.php`

Expand Down Expand Up @@ -36201,19 +36201,19 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
- new HashTable (line 127)
- new Variable (line 129)
- new Variable (line 307)
- new HashTable (line 367)
- new Variable (line 368)
- new Variable (line 384)
- new Variable (line 521)
- new HashTable (line 541)
- new Variable (line 550)
- new HashTable (line 567)
- new Variable (line 569)
- new HashTable (line 579)
- new Variable (line 581)
- new Variable (line 601)
- new HashTable (line 606)
- new Variable (line 608)
- new HashTable (line 408)
- new Variable (line 409)
- new Variable (line 425)
- new Variable (line 562)
- new HashTable (line 582)
- new Variable (line 591)
- new HashTable (line 608)
- new Variable (line 610)
- new HashTable (line 620)
- new Variable (line 622)
- new Variable (line 642)
- new HashTable (line 647)
- new Variable (line 649)
- 28 class method(s)

### `ext/standard/VmStreamContextOptions.php`
Expand Down Expand Up @@ -37851,13 +37851,13 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `ext/standard/diskfreespace.php`

**Warnings** (review for bootstrap subset):
- new disk_free_space (line 23)
- new disk_free_space (line 24)
- 2 class method(s)

### `ext/standard/disktotalspace.php`

**Warnings** (review for bootstrap subset):
- new disk_total_space (line 23)
- new disk_total_space (line 24)
- 2 class method(s)

### `ext/standard/dl.php`
Expand Down Expand Up @@ -40833,8 +40833,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `ext/standard/stream_context_set_option.php`

**Warnings** (review for bootstrap subset):
- new Variable (line 65)
- 3 class method(s)
- 2 class method(s)
- 1 closure(s)

### `ext/standard/stream_context_set_options.php`
Expand Down Expand Up @@ -43453,7 +43452,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `ext/xml/xml_error_string.php`

**Warnings** (review for bootstrap subset):
- new ArgumentCountError (line 26)
- new ArgumentCountError (line 31)
- 2 class method(s)

### `ext/xml/xml_get_current_byte_index.php`
Expand Down Expand Up @@ -45870,8 +45869,8 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `lib/BuiltinParamNames.php`

**Warnings** (review for bootstrap subset):
- new ArgumentCountError (line 2393)
- new ArgumentCountError (line 2404)
- new ArgumentCountError (line 2396)
- new ArgumentCountError (line 2407)
- 19 class method(s)
- 1 closure(s)

Expand Down
215 changes: 189 additions & 26 deletions ext/standard/JitStreamContextSetOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@

namespace PHPCompiler\ext\standard;

use PHPCompiler\JIT\BasicBlockHelper;
use PHPCompiler\JIT\Builtin\StreamContextRuntime;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\ExceptionBridge;
use PHPCompiler\JIT\HashTableHelper;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable as VmVariable;
use PHPLLVM\Builder;
use PHPLLVM\Value;

/** LLVM lowering for stream_context_set_option() (#3448). */
/** LLVM lowering for stream_context_set_option() (#3448, #30645). */
final class JitStreamContextSetOption
{
private static int $guardSeq = 0;

public static function invoke(Context $context, JITVariable ...$args): Value
{
// Arity checked by stream_context_set_option::call via requireArgCountRangeJit (#30584).
Expand All @@ -23,40 +29,197 @@ public static function invoke(Context $context, JITVariable ...$args): Value

JitStreamContextRequiredArg::validate($context, $args[0], 'stream_context_set_option', 1);

$ctxHt = self::loadContextArray($context, $args[0]);
if (2 === $argc) {
$optHt = self::loadOptionsArray($context, $args[1], 2);
$context->builder->call(
$context->lookupFunction('__phpc_stream_context_merge_options'),
$ctxHt,
$optHt
);
} else {
// 3 or 4 args: singular form; omitted value is null (#30584 / php-src stub).
$wrapperVal = self::loadValuePointer($context, $args[1], 2);
$optionVal = self::loadValuePointer($context, $args[2], 3);
if (4 === $argc) {
$valueVal = self::loadValuePointer($context, $args[3], 4);
} else {
$nullSlot = JitValueBox::alloc($context);
$valueVal = JitValueBox::pointer($context, $nullSlot);
$context->builder->call($context->lookupFunction('__value__writeNull'), $valueVal);
$arg3 = $argc >= 3 ? $args[2] : null;
$arg4 = $argc >= 4 ? $args[3] : null;
$optionIsNull = self::operandIsNull($arg3);
$valueProvided = null !== $arg4;

if (self::isCompileTimeArray($args[1])) {
if (!$optionIsNull) {
return self::emitValueError($context, VmStreamContext::SET_OPTION_OPTION_NAME_MUST_BE_NULL_ON_ARRAY);
}
$context->builder->call(
$context->lookupFunction('__phpc_stream_context_set_single_option'),
$ctxHt,
$wrapperVal,
$optionVal,
$valueVal
);
if ($valueProvided) {
return self::emitValueError($context, VmStreamContext::SET_OPTION_VALUE_FORBIDDEN_ON_ARRAY);
}

return self::emitMergeOptions($context, $args[0], $args[1]);
}

if (self::isCompileTimeNonArray($args[1])) {
if ($optionIsNull) {
return self::emitValueError($context, VmStreamContext::SET_OPTION_OPTION_NAME_NULL_ON_STRING);
}
if (!$valueProvided) {
return self::emitValueError($context, VmStreamContext::SET_OPTION_VALUE_REQUIRED_ON_STRING);
}

return self::emitSetSingleOption($context, $args[0], $args[1], $args[2], $args[3]);
}

return self::emitRuntimeWrapperDispatch(
$context,
$args[0],
$args[1],
$arg3,
$arg4,
$optionIsNull,
$valueProvided
);
}

private static function emitRuntimeWrapperDispatch(
Context $context,
JITVariable $ctxArg,
JITVariable $wrapperArg,
?JITVariable $arg3,
?JITVariable $arg4,
bool $optionIsNull,
bool $valueProvided
): Value {
$tag = 'sctxso'.(string) ++self::$guardSeq;
$isArray = self::emitRuntimeIsArray($context, $wrapperArg);
$arrBb = BasicBlockHelper::append($context, 'sctx_set_opt_arr_'.$tag);
$strBb = BasicBlockHelper::append($context, 'sctx_set_opt_str_'.$tag);
$joinBb = BasicBlockHelper::append($context, 'sctx_set_opt_join_'.$tag);
$context->builder->branchIf($isArray, $arrBb, $strBb);

$context->builder->positionAtEnd($arrBb);
if (!$optionIsNull) {
ExceptionBridge::emitValueErrorAndAbort($context, VmStreamContext::SET_OPTION_OPTION_NAME_MUST_BE_NULL_ON_ARRAY);
BasicBlockHelper::ensureOpenInsertBlock($context, 'sctx_set_opt_arr_opt_dead_'.$tag);
} elseif ($valueProvided) {
ExceptionBridge::emitValueErrorAndAbort($context, VmStreamContext::SET_OPTION_VALUE_FORBIDDEN_ON_ARRAY);
BasicBlockHelper::ensureOpenInsertBlock($context, 'sctx_set_opt_arr_val_dead_'.$tag);
} else {
self::emitMergeOptionsVoid($context, $ctxArg, $wrapperArg);
}
$context->builder->branch($joinBb);

$context->builder->positionAtEnd($strBb);
if ($optionIsNull) {
ExceptionBridge::emitValueErrorAndAbort($context, VmStreamContext::SET_OPTION_OPTION_NAME_NULL_ON_STRING);
BasicBlockHelper::ensureOpenInsertBlock($context, 'sctx_set_opt_str_opt_dead_'.$tag);
} elseif (!$valueProvided) {
ExceptionBridge::emitValueErrorAndAbort($context, VmStreamContext::SET_OPTION_VALUE_REQUIRED_ON_STRING);
BasicBlockHelper::ensureOpenInsertBlock($context, 'sctx_set_opt_str_val_dead_'.$tag);
} else {
assert(null !== $arg3 && null !== $arg4);
self::emitSetSingleOptionVoid($context, $ctxArg, $wrapperArg, $arg3, $arg4);
}
$context->builder->branch($joinBb);

$context->builder->positionAtEnd($joinBb);

return self::emitTrue($context);
}

private static function emitRuntimeIsArray(Context $context, JITVariable $arg): Value
{
$ptr = self::loadValuePointer($context, $arg, 2);
$map = $context->structFieldMap['__value__'];
$typeByte = $context->builder->load(
$context->builder->structGep($ptr, $map['type'])
);
$i8 = $context->getTypeFromString('int8');
$kind = $context->builder->and($typeByte, $i8->constInt(0x7f, false));
$isArray = $context->builder->icmp(
Builder::INT_EQ,
$kind,
$i8->constInt(VmVariable::TYPE_ARRAY, false)
);
$isHt = $context->builder->icmp(
Builder::INT_EQ,
$kind,
$i8->constInt(JITVariable::TYPE_HASHTABLE & 0x7f, false)
);

return $context->builder->or($isArray, $isHt);
}

private static function emitMergeOptions(Context $context, JITVariable $ctxArg, JITVariable $optArg): Value
{
self::emitMergeOptionsVoid($context, $ctxArg, $optArg);

return self::emitTrue($context);
}

private static function emitMergeOptionsVoid(Context $context, JITVariable $ctxArg, JITVariable $optArg): void
{
$ctxHt = self::loadContextArray($context, $ctxArg);
$optHt = self::loadOptionsArray($context, $optArg, 2);
$context->builder->call(
$context->lookupFunction('__phpc_stream_context_merge_options'),
$ctxHt,
$optHt
);
}

private static function emitSetSingleOption(
Context $context,
JITVariable $ctxArg,
JITVariable $wrapperArg,
JITVariable $optionArg,
JITVariable $valueArg
): Value {
self::emitSetSingleOptionVoid($context, $ctxArg, $wrapperArg, $optionArg, $valueArg);

return self::emitTrue($context);
}

private static function emitSetSingleOptionVoid(
Context $context,
JITVariable $ctxArg,
JITVariable $wrapperArg,
JITVariable $optionArg,
JITVariable $valueArg
): void {
$ctxHt = self::loadContextArray($context, $ctxArg);
$wrapperVal = self::loadValuePointer($context, $wrapperArg, 2);
$optionVal = self::loadValuePointer($context, $optionArg, 3);
$valueVal = self::loadValuePointer($context, $valueArg, 4);
$context->builder->call(
$context->lookupFunction('__phpc_stream_context_set_single_option'),
$ctxHt,
$wrapperVal,
$optionVal,
$valueVal
);
}

private static function emitValueError(Context $context, string $message): Value
{
ExceptionBridge::emitValueErrorAndAbort($context, $message);
BasicBlockHelper::ensureOpenInsertBlock($context, 'sctx_set_opt_ve_dead_'.(string) ++self::$guardSeq);

return self::emitTrue($context);
}

private static function emitTrue(Context $context): Value
{
$slot = JitValueBox::alloc($context);
JitValueBox::writeBool($context, $slot, $context->constantFromBool(true));

return JitValueBox::pointer($context, $slot);
}

private static function isCompileTimeArray(JITVariable $arg): bool
{
return JITVariable::TYPE_HASHTABLE === $arg->type
|| 0 !== ($arg->type & JITVariable::IS_NATIVE_ARRAY);
}

private static function isCompileTimeNonArray(JITVariable $arg): bool
{
return !self::isCompileTimeArray($arg) && JITVariable::TYPE_VALUE !== $arg->type;
}

private static function operandIsNull(?JITVariable $arg): bool
{
return null === $arg
|| JITVariable::TYPE_NULL === $arg->type
|| $arg->isNullConstant;
}

private static function loadContextArray(Context $context, JITVariable $arg): Value
{
if (JITVariable::TYPE_HASHTABLE === $arg->type) {
Expand Down
Loading
Loading