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
175 changes: 101 additions & 74 deletions docs/bootstrap-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Regenerate: `php script/bootstrap-inventory.php`

| Metric | Count |
|--------|------:|
| PHP files on vm.php path | 212 |
| PHP files on vm.php path | 214 |
| Source constructs flagged (blockers) | 10 |
| Source constructs flagged (warnings) | 553 |
| Source constructs flagged (warnings) | 570 |

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

Expand Down Expand Up @@ -162,7 +162,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `ext/types/strlen.php` | 0 | 1 |
| `lib/AOT/Linker.php` | 8 | 1 |
| `lib/Block.php` | 0 | 4 |
| `lib/Compiler.php` | 0 | 57 |
| `lib/Compiler.php` | 0 | 68 |
| `lib/Frame.php` | 0 | 1 |
| `lib/Func.php` | 0 | 1 |
| `lib/Func/Internal.php` | 0 | 2 |
Expand Down Expand Up @@ -199,6 +199,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/JIT/Call.php` | 0 | 1 |
| `lib/JIT/Call/Native.php` | 0 | 1 |
| `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/Helper.php` | 0 | 7 |
Expand All @@ -215,7 +216,8 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/Lint/IncrementDetector.php` | 0 | 4 |
| `lib/Lint/Issue.php` | 0 | 2 |
| `lib/Lint/LintCompiler.php` | 0 | 6 |
| `lib/Lint/Linter.php` | 0 | 5 |
| `lib/Lint/Linter.php` | 0 | 6 |
| `lib/Lint/ListDestructuringDetector.php` | 0 | 4 |
| `lib/Lint/UnsupportedRegistry.php` | 0 | 1 |
| `lib/Module.php` | 0 | 1 |
| `lib/ModuleAbstract.php` | 0 | 1 |
Expand Down Expand Up @@ -1046,63 +1048,74 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
### `lib/Compiler.php`

**Warnings** (review for bootstrap subset):
- new Func\PHP (line 41)
- new Block (line 46)
- new OpCode (line 100)
- new Block (line 109)
- new OpCode (line 119)
- new Variable (line 134)
- new Operand\Temporary (line 135)
- new OpCode (line 157)
- new Operand\Literal (line 168)
- new OpCode (line 170)
- new OpCode (line 185)
- new OpCode (line 193)
- new OpCode (line 213)
- new OpCode (line 218)
- new OpCode (line 251)
- new OpCode (line 260)
- new OpCode (line 353)
- new OpCode (line 360)
- new OpCode (line 374)
- new OpCode (line 380)
- new OpCode (line 398)
- new OpCode (line 404)
- new OpCode (line 410)
- new OpCode (line 421)
- new OpCode (line 429)
- new OpCode (line 435)
- new OpCode (line 441)
- new OpCode (line 446)
- new OpCode (line 453)
- new OpCode (line 460)
- new OpCode (line 466)
- new OpCode (line 471)
- new OpCode (line 478)
- new OpCode (line 485)
- new OpCode (line 490)
- new OpCode (line 495)
- new OpCode (line 504)
- new OpCode (line 509)
- new OpCode (line 516)
- new OpCode (line 525)
- new OpCode (line 544)
- new Block (line 560)
- new Func\PHP (line 42)
- new Block (line 47)
- new OpCode (line 103)
- new Block (line 112)
- new OpCode (line 122)
- new Variable (line 137)
- new Operand\Temporary (line 138)
- new OpCode (line 160)
- new Operand\Literal (line 171)
- new OpCode (line 173)
- new OpCode (line 188)
- new OpCode (line 196)
- new OpCode (line 216)
- new OpCode (line 221)
- new OpCode (line 254)
- new OpCode (line 263)
- new OpCode (line 356)
- new OpCode (line 363)
- new OpCode (line 377)
- new OpCode (line 383)
- new OpCode (line 401)
- new OpCode (line 407)
- new OpCode (line 413)
- new OpCode (line 424)
- new OpCode (line 432)
- new OpCode (line 438)
- new OpCode (line 444)
- new OpCode (line 449)
- new OpCode (line 456)
- new OpCode (line 463)
- new OpCode (line 469)
- new OpCode (line 474)
- new OpCode (line 481)
- new OpCode (line 488)
- new OpCode (line 493)
- new OpCode (line 498)
- new OpCode (line 507)
- new OpCode (line 512)
- new OpCode (line 519)
- new OpCode (line 528)
- new OpCode (line 547)
- new Block (line 559)
- new Block (line 563)
- new OpCode (line 566)
- new OpCode (line 572)
- new OpCode (line 586)
- new Block (line 593)
- new OpCode (line 596)
- new OpCode (line 606)
- new Operand\Temporary (line 616)
- new Variable (line 624)
- new Operand\Temporary (line 626)
- new Variable (line 687)
- new OpCode (line 717)
- new OpCode (line 723)
- new OpCode (line 727)
- 25 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- new OpCode (line 567)
- new Block (line 574)
- new OpCode (line 582)
- new OpCode (line 589)
- new OpCode (line 597)
- new OpCode (line 603)
- new OpCode (line 611)
- new OpCode (line 614)
- new OpCode (line 620)
- new Block (line 674)
- new Block (line 677)
- new OpCode (line 680)
- new OpCode (line 686)
- new OpCode (line 700)
- new Block (line 707)
- new OpCode (line 710)
- new OpCode (line 720)
- new Temporary (line 730)
- new Variable (line 740)
- new Operand\Temporary (line 742)
- new Variable (line 803)
- new OpCode (line 833)
- new OpCode (line 839)
- new OpCode (line 843)
- 28 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Frame.php`

Expand Down Expand Up @@ -1142,13 +1155,13 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
- new Variable (line 139)
- new JIT\Call\Vararg (line 146)
- new JIT\Call\Native (line 149)
- new ext\standard\boolval (line 249)
- new Variable (line 413)
- new Variable (line 734)
- new Operand\Literal (line 804)
- new Operand\Literal (line 808)
- new Operand\Literal (line 812)
- 11 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- new ext\standard\boolval (line 263)
- new Variable (line 427)
- new Variable (line 773)
- new Operand\Literal (line 843)
- new Operand\Literal (line 847)
- new Operand\Literal (line 851)
- 12 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/JIT/Analyzer.php`

Expand Down Expand Up @@ -1312,6 +1325,11 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
- 1 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler
- 1 closure(s)

### `lib/JIT/CoalesceHelper.php`

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

### `lib/JIT/Context.php`

**Warnings** (review for bootstrap subset):
Expand Down Expand Up @@ -1450,11 +1468,20 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:

**Warnings** (review for bootstrap subset):
- new Runtime (line 24)
- new IncrementDetector (line 44)
- new State (line 86)
- new LintCompiler (line 101)
- new IncrementDetector (line 45)
- new ListDestructuringDetector (line 46)
- new State (line 89)
- new LintCompiler (line 104)
- 9 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Lint/ListDestructuringDetector.php`

**Warnings** (review for bootstrap subset):
- new ParserFactory (line 27)
- new NodeTraverser (line 54)
- new Issue (line 61)
- 2 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Lint/UnsupportedRegistry.php`

**Warnings** (review for bootstrap subset):
Expand Down Expand Up @@ -1510,10 +1537,10 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
**Warnings** (review for bootstrap subset):
- new Variable (line 64)
- new Variable (line 69)
- new Func\PHP (line 217)
- new ClassEntry (line 267)
- new ObjectEntry (line 279)
- new VM\ClassProperty (line 369)
- new Func\PHP (line 224)
- new ClassEntry (line 274)
- new ObjectEntry (line 286)
- new VM\ClassProperty (line 376)
- 3 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/VM/ClassEntry.php`
Expand Down
1 change: 0 additions & 1 deletion docs/unsupported-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Some constructs (for example destructuring assigns and prefix/postfix `++`/`--`)
| CFG kind | Tracking |
|----------|----------|
| `Stmt_Foreach` | [#53](https://github.com/PurHur/php-compiler/issues/53) |
| `Expr_BinaryOp_Coalesce` (`??`) | [#99](https://github.com/PurHur/php-compiler/issues/99) |
| `Expr_Throw`, `Stmt_Try`, `Stmt_Catch`, `Stmt_Finally` | [#195](https://github.com/PurHur/php-compiler/issues/195) |
| `Expr_Ternary` (`?:`) | [#114](https://github.com/PurHur/php-compiler/issues/114) |
| `Expr_AssignOp_*`, `Expr_BinaryOp_ShiftLeft` / `ShiftRight` (compound assign) | [#136](https://github.com/PurHur/php-compiler/issues/136) |
Expand Down
118 changes: 117 additions & 1 deletion lib/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPCfg\Op;
use PHPCfg\Block as CfgBlock;
use PHPCfg\Operand;
use PHPCfg\Operand\Temporary;
use PHPCfg\Script;
use PHPTypes\Type;
use PHPCompiler\VM\Variable;
Expand Down Expand Up @@ -81,6 +82,8 @@ protected function compileOps(array $ops, Block $block): void {
default:
if ($child instanceof Op\Expr\Isset_ && count($child->vars) > 1) {
$block = $this->compileIssetMulti($child, $block);
} elseif ($child instanceof Op\Expr\BinaryOp\Coalesce) {
$block = $this->compileCoalesce($child, $block);
} else {
$this->compileOp($child, $block);
}
Expand Down Expand Up @@ -549,6 +552,117 @@ protected function compileIsset(Op\Expr\Isset_ $expr, Block $block): array
)];
}

protected function compileCoalesce(Op\Expr\BinaryOp\Coalesce $expr, Block $block): Block
{
$resultSlot = $this->compileOperand($expr->result, $block, false);

$endBlock = new Block($block->orig);
$endBlock->inheritUndefinedLocals = true;
$endBlock->inheritScopeFrom($block);

$rightBlock = new Block($block->orig);
$rightBlock->inheritUndefinedLocals = true;
$rightBlock->inheritScopeFrom($block);
$rightSlot = $this->compileOperand($expr->right, $rightBlock, true);
$rightBlock->addOpCode(new OpCode(
OpCode::TYPE_ASSIGN,
$resultSlot,
$resultSlot,
$rightSlot
));

$leftBlock = new Block($block->orig);
$leftBlock->inheritUndefinedLocals = true;
$leftBlock->inheritScopeFrom($block);

$checkSlot = $this->compileBoolTemporary($block);
$issetTarget = $this->resolveCoalesceIssetTarget($expr->left, $block);
if (null !== $issetTarget) {
[$containerSlot, $dimSlot] = $issetTarget;
$block->addOpCode(new OpCode(
OpCode::TYPE_ISSET,
$checkSlot,
$containerSlot,
$dimSlot
));
$leftSlot = $this->compileOperand($expr->left, $leftBlock, true);
$leftBlock->addOpCode(new OpCode(
OpCode::TYPE_ASSIGN,
$resultSlot,
$resultSlot,
$leftSlot
));
} else {
$leftSlot = $this->compileOperand($expr->left, $block, true);
$block->addOpCode(new OpCode(
OpCode::TYPE_ASSIGN,
$resultSlot,
$resultSlot,
$leftSlot
));
$block->addOpCode(new OpCode(
OpCode::TYPE_ISSET,
$checkSlot,
$leftSlot,
null
));
}

$leftJump = new OpCode(OpCode::TYPE_JUMP);
$leftJump->block1 = $endBlock;
$leftBlock->addOpCode($leftJump);
$rightJump = new OpCode(OpCode::TYPE_JUMP);
$rightJump->block1 = $endBlock;
$rightBlock->addOpCode($rightJump);
$endBlock->parents[] = $leftBlock;
$endBlock->parents[] = $rightBlock;

$coalesceOp = new OpCode(
OpCode::TYPE_COALESCE,
$resultSlot,
$checkSlot
);
$coalesceOp->block1 = $leftBlock;
$coalesceOp->block2 = $rightBlock;
$coalesceOp->block3 = $endBlock;
$block->addOpCode($coalesceOp);

return $endBlock;
}

/**
* @return ?array{0: int, 1: ?int}
*/
protected function resolveCoalesceIssetTarget(Operand $operand, Block $block): ?array
{
if (null !== $this->unwrapArrayDimFetch($operand)) {
return $this->resolveIssetTarget($operand, $block);
}
if (null !== $this->unwrapVariableOperand($operand)) {
return $this->resolveIssetTarget($operand, $block);
}

return null;
}

protected function unwrapVariableOperand(Operand $operand): ?Operand\Variable
{
while ($operand instanceof Temporary) {
if ($operand->original instanceof Operand\Variable) {
return $operand->original;
}
if (null === $operand->original) {
return null;
}
$operand = $operand->original;
}
if ($operand instanceof Operand\Variable) {
return $operand;
}

return null;
}

/**
* isset($a, $b, …) with short-circuit evaluation (PHP semantics).
* Returns the block where compilation should continue.
Expand Down Expand Up @@ -613,8 +727,10 @@ protected function compileIssetMulti(Op\Expr\Isset_ $expr, Block $block): Block

protected function compileBoolTemporary(Block $block): int
{
$operand = new Operand\Temporary;
$operand = new Temporary;
$operand->type = Type::bool();
// JIT assignOperandValue skips operands with empty usages (#99 coalesce branches).
$operand->usages[] = $operand;

return $block->getVarSlot($operand, false);
}
Expand Down
Loading
Loading