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
20 changes: 10 additions & 10 deletions docs/bootstrap-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/JIT/Call/Vararg.php` | 0 | 2 |
| `lib/JIT/CoalesceHelper.php` | 0 | 1 |
| `lib/JIT/Context.php` | 0 | 15 |
| `lib/JIT/HashTableHelper.php` | 0 | 1 |
| `lib/JIT/HashTableHelper.php` | 0 | 2 |
| `lib/JIT/Helper.php` | 0 | 7 |
| `lib/JIT/IssetHelper.php` | 0 | 1 |
| `lib/JIT/JitNativeString.php` | 0 | 5 |
Expand All @@ -237,7 +237,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/JIT/StringOffsetHelper.php` | 0 | 1 |
| `lib/JIT/SuperglobalInit.php` | 0 | 3 |
| `lib/JIT/ValueEchoHelper.php` | 0 | 1 |
| `lib/JIT/Variable.php` | 0 | 18 |
| `lib/JIT/Variable.php` | 0 | 17 |
| `lib/Lint/IncrementDetector.php` | 0 | 4 |
| `lib/Lint/Issue.php` | 0 | 2 |
| `lib/Lint/LintCompiler.php` | 0 | 6 |
Expand Down Expand Up @@ -1314,10 +1314,10 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
- new JIT\Call\Native (line 149)
- new ext\standard\boolval (line 268)
- new Variable (line 432)
- new Variable (line 821)
- new Operand\Literal (line 891)
- new Operand\Literal (line 895)
- new Operand\Literal (line 899)
- new Variable (line 827)
- new Operand\Literal (line 897)
- new Operand\Literal (line 901)
- new Operand\Literal (line 905)
- 12 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/JIT/Analyzer.php`
Expand Down Expand Up @@ -1526,7 +1526,8 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
### `lib/JIT/HashTableHelper.php`

**Warnings** (review for bootstrap subset):
- 9 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- new Variable (line 201)
- 11 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/JIT/Helper.php`

Expand Down Expand Up @@ -1556,7 +1557,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
### `lib/JIT/JitValueBox.php`

**Warnings** (review for bootstrap subset):
- 4 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- 5 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/JIT/JitValueCompare.php`

Expand Down Expand Up @@ -1615,8 +1616,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
- new Variable (line 413)
- new Variable (line 430)
- new Variable (line 450)
- new Variable (line 463)
- new Variable (line 475)
- new Variable (line 464)
- 15 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Lint/IncrementDetector.php`
Expand Down
2 changes: 1 addition & 1 deletion docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Auto-generated by `script/capability-matrix.php`. Do not edit by hand.
|----------|:--:|:---:|:---:|--------|-------|
| `abs` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `array_combine` | yes | no | no | standard | doc: VM only; not implemented for JIT in this compiler build |
| `array_fill` | yes | yes | yes | standard | JIT PHPT |
| `array_fill` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `array_flip` | yes | no | no | standard | doc: VM only; not implemented for JIT in this compiler build |
| `array_key_exists` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `array_keys` | yes | yes | yes | standard | doc: VM only |
Expand Down
14 changes: 10 additions & 4 deletions lib/JIT.php
Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,11 @@ private function assignOperand(Operand $result, Variable $value): void {

return;
case Variable::TYPE_VALUE:
$loaded = $this->context->builder->load($this->context->helper->loadValue($value));
$this->context->builder->store($loaded, $result->value);
JIT\JitValueBox::copyFromPointer(
$this->context,
$valueRef,
$this->context->helper->loadValue($value)
);

return;
default:
Expand Down Expand Up @@ -797,8 +800,11 @@ private function assignOperand(Operand $result, Variable $value): void {

return;
} elseif (Variable::TYPE_VALUE === $result->type && Variable::TYPE_VALUE === $value->type) {
$loaded = $this->context->builder->load($this->context->helper->loadValue($value));
$this->context->builder->store($loaded, $result->value);
JIT\JitValueBox::copyFromPointer(
$this->context,
$result->value,
$this->context->helper->loadValue($value)
);

return;
}
Expand Down
71 changes: 69 additions & 2 deletions lib/JIT/HashTableHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,87 @@ public static function buildArrayFill(
return $ht;
}

public static function readStringAt(Context $context, Value $ht, Value $index): Value
public static function listEntryPointer(Context $context, Value $ht, Value $index): Value
{
$map = $context->structFieldMap['__hashtable__'];
$values = $context->builder->load(
$context->builder->structGep($ht, $map['values'])
);
$entry = $context->builder->inBoundsGep($values, $index);

return $context->builder->inBoundsGep($values, $index);
}

public static function readStringAt(Context $context, Value $ht, Value $index): Value
{
$entry = self::listEntryPointer($context, $ht, $index);

return $context->builder->call(
$context->lookupFunction('__value__readString'),
$entry
);
}

/**
* Read a packed-list element into a stack {@see __value__} slot (for echo / mixed-type index).
*/
public static function readIndexedToValueBox(Context $context, Value $ht, Value $index): Variable
{
static $seq = 0;
$tag = 'rb'.(string) ++$seq;
$slot = JitValueBox::alloc($context);
$destPtr = JitValueBox::pointer($context, $slot);
$entryPtr = self::listEntryPointer($context, $ht, $index);
$valueMap = $context->structFieldMap['__value__'];
$typeByte = $context->builder->load(
$context->builder->structGep($entryPtr, $valueMap['type'])
);
$i8 = $context->getTypeFromString('int8');

$stringBlock = BasicBlockHelper::append($context, 'ht_rb_string_'.$tag);
$longBlock = BasicBlockHelper::append($context, 'ht_rb_long_'.$tag);
$done = BasicBlockHelper::append($context, 'ht_rb_done_'.$tag);

$isString = $context->builder->icmp(
Builder::INT_EQ,
$typeByte,
$i8->constInt(Variable::TYPE_STRING, false)
);
$context->builder->branchIf($isString, $stringBlock, $longBlock);

$context->builder->positionAtEnd($stringBlock);
$str = $context->builder->call(
$context->lookupFunction('__value__readString'),
$entryPtr
);
$owned = $context->builder->call(
$context->lookupFunction('__string__separate'),
$str
);
$context->builder->call(
$context->lookupFunction('__value__writeString'),
$destPtr,
$owned
);
$context->builder->branch($done);

$context->builder->positionAtEnd($longBlock);
$context->builder->call(
$context->lookupFunction('__value__writeLong'),
$destPtr,
$context->builder->call($context->lookupFunction('__value__readLong'), $entryPtr)
);
$context->builder->branch($done);

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

return new Variable(
$context,
Variable::TYPE_VALUE,
Variable::KIND_VARIABLE,
$slot
);
}

public static function initArray(Context $context, Variable $result): void
{
$result->nextFreeElement = 0;
Expand Down
116 changes: 116 additions & 0 deletions lib/JIT/JitValueBox.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

final class JitValueBox
{
private static int $copySeq = 0;

public static function alloc(Context $context): Value
{
return $context->builder->alloca($context->getTypeFromString('__value__'));
Expand All @@ -35,6 +37,120 @@ public static function writeLong(Context $context, Value $slot, Value $long): vo
);
}

/**
* Copy a boxed value from a {@see __value__*} slot into a stack {@see __value__} alloca.
*/
public static function copyFromPointer(Context $context, Value $destSlot, Value $srcPtr): void
{
$map = $context->structFieldMap['__value__'];
$typeByte = $context->builder->load(
$context->builder->structGep($srcPtr, $map['type'])
);
$i8 = $context->getTypeFromString('int8');
$destPtr = self::pointer($context, $destSlot);

$tag = 'v'.(string) self::$copySeq++;
$stringBlock = BasicBlockHelper::append($context, 'value_copy_string_'.$tag);
$longBlock = BasicBlockHelper::append($context, 'value_copy_long_'.$tag);
$doubleBlock = BasicBlockHelper::append($context, 'value_copy_double_'.$tag);
$boolBlock = BasicBlockHelper::append($context, 'value_copy_bool_'.$tag);
$nullBlock = BasicBlockHelper::append($context, 'value_copy_null_'.$tag);
$done = BasicBlockHelper::append($context, 'value_copy_done_'.$tag);

$isString = $context->builder->icmp(
Builder::INT_EQ,
$typeByte,
$i8->constInt(Variable::TYPE_STRING, false)
);
$isLong = $context->builder->icmp(
Builder::INT_EQ,
$typeByte,
$i8->constInt(Variable::TYPE_NATIVE_LONG, false)
);
$isBool = $context->builder->icmp(
Builder::INT_EQ,
$typeByte,
$i8->constInt(Variable::TYPE_NATIVE_BOOL, false)
);
$isDouble = $context->builder->icmp(
Builder::INT_EQ,
$typeByte,
$i8->constInt(Variable::TYPE_NATIVE_DOUBLE, false)
);
$isNull = $context->builder->icmp(
Builder::INT_EQ,
$typeByte,
$i8->constInt(Variable::TYPE_NULL, false)
);

$afterString = BasicBlockHelper::append($context, 'value_copy_after_string_'.$tag);
$context->builder->branchIf($isString, $stringBlock, $afterString);

$context->builder->positionAtEnd($stringBlock);
$str = $context->builder->call(
$context->lookupFunction('__value__readString'),
$srcPtr
);
$owned = $context->builder->call(
$context->lookupFunction('__string__separate'),
$str
);
$context->builder->call(
$context->lookupFunction('__value__writeString'),
$destPtr,
$owned
);
$context->builder->branch($done);

$context->builder->positionAtEnd($afterString);
$afterLong = BasicBlockHelper::append($context, 'value_copy_after_long_'.$tag);
$context->builder->branchIf($isLong, $longBlock, $afterLong);

$context->builder->positionAtEnd($longBlock);
$context->builder->call(
$context->lookupFunction('__value__writeLong'),
$destPtr,
$context->builder->call($context->lookupFunction('__value__readLong'), $srcPtr)
);
$context->builder->branch($done);

$context->builder->positionAtEnd($afterLong);
$afterBool = BasicBlockHelper::append($context, 'value_copy_after_bool_'.$tag);
$context->builder->branchIf($isBool, $boolBlock, $afterBool);

$context->builder->positionAtEnd($boolBlock);
self::writeBool(
$context,
$destSlot,
$context->builder->truncOrBitCast(
$context->builder->call($context->lookupFunction('__value__readLong'), $srcPtr),
$context->getTypeFromString('int1')
)
);
$context->builder->branch($done);

$context->builder->positionAtEnd($afterBool);
$afterDouble = BasicBlockHelper::append($context, 'value_copy_after_double_'.$tag);
$context->builder->branchIf($isDouble, $doubleBlock, $afterDouble);

$context->builder->positionAtEnd($doubleBlock);
$context->builder->call(
$context->lookupFunction('__value__writeDouble'),
$destPtr,
$context->builder->call($context->lookupFunction('__value__readDouble'), $srcPtr)
);
$context->builder->branch($done);

$context->builder->positionAtEnd($afterDouble);
$context->builder->branchIf($isNull, $nullBlock, $done);

$context->builder->positionAtEnd($nullBlock);
$context->builder->call($context->lookupFunction('__value__writeNull'), $destPtr);
$context->builder->branch($done);

$context->builder->positionAtEnd($done);
}

public static function writeBool(Context $context, Value $slot, Value $bool): void
{
$map = $context->structFieldMap['__value__'];
Expand Down
13 changes: 1 addition & 12 deletions lib/JIT/Variable.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,18 +454,7 @@ public function dimFetch(self $dim, ?Type $expectedType = null): Variable {
$str
);
}
$long = $this->context->builder->call(
$this->context->lookupFunction('__hashtable__readLongAt'),
$ht,
$index
);

return new Variable(
$this->context,
self::TYPE_NATIVE_LONG,
self::KIND_VALUE,
$long
);
return HashTableHelper::readIndexedToValueBox($this->context, $ht, $index);
default:
if (!$this->type & self::IS_NATIVE_ARRAY) {
throw new \LogicException("Unsupported dim fetch on " . self::getStringType($this->type));
Expand Down
15 changes: 15 additions & 0 deletions test/fixtures/aot/cases/array_fill.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
AOT: array_fill() with string values (packed list)
--FILE--
<?php
$a = array_fill(0, 3, 'x');
echo count($a), "\n";
echo $a[0], $a[1], $a[2], "\n";
$b = array_fill(2, 2, 7);
echo $b[2], '|', $b[3], "\n";
--EXPECT--
3
xxx
7|7
--EXPECT_EXIT--
0
Loading