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
86 changes: 65 additions & 21 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 | 241 |
| PHP files on vm.php path | 246 |
| Source constructs flagged (blockers) | 10 |
| Source constructs flagged (warnings) | 631 |
| Source constructs flagged (warnings) | 650 |

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

Expand Down Expand Up @@ -59,7 +59,8 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `ext/standard/JitStripTags.php` | 0 | 1 |
| `ext/standard/JitStrpos.php` | 0 | 1 |
| `ext/standard/JitUrlencode.php` | 0 | 1 |
| `ext/standard/Module.php` | 0 | 111 |
| `ext/standard/JitWebParams.php` | 0 | 11 |
| `ext/standard/Module.php` | 0 | 114 |
| `ext/standard/VmDate.php` | 0 | 1 |
| `ext/standard/VmExit.php` | 0 | 2 |
| `ext/standard/VmFs.php` | 0 | 3 |
Expand Down Expand Up @@ -175,6 +176,9 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `ext/standard/ucwords.php` | 0 | 1 |
| `ext/standard/urldecode.php` | 0 | 1 |
| `ext/standard/urlencode.php` | 0 | 1 |
| `ext/standard/web_bool.php` | 0 | 1 |
| `ext/standard/web_int.php` | 0 | 1 |
| `ext/standard/web_string.php` | 0 | 1 |
| `ext/types/Module.php` | 0 | 13 |
| `ext/types/is_type.php` | 0 | 1 |
| `ext/types/mb_strlen.php` | 0 | 1 |
Expand Down Expand Up @@ -262,6 +266,7 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/VM/Refcount.php` | 0 | 1 |
| `lib/VM/Variable.php` | 0 | 4 |
| `lib/Web/DevServer.php` | 0 | 1 |
| `lib/Web/Params.php` | 0 | 2 |
| `lib/Web/ProjectManifest.php` | 0 | 1 |
| `lib/Web/ResponseContext.php` | 0 | 2 |
| `lib/Web/Superglobals.php` | 0 | 6 |
Expand Down Expand Up @@ -404,6 +409,21 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
**Warnings** (review for bootstrap subset):
- 4 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `ext/standard/JitWebParams.php`

**Warnings** (review for bootstrap subset):
- new array_key_exists (line 47)
- new JITVariable (line 65)
- new is_numeric (line 66)
- new array_key_exists (line 126)
- new JITVariable (line 144)
- new string_trim (line 145)
- new JITVariable (line 150)
- new JITVariable (line 151)
- new JITVariable (line 157)
- new substr (line 163)
- 3 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `ext/standard/Module.php`

**Warnings** (review for bootstrap subset):
Expand Down Expand Up @@ -499,24 +519,27 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
- new header_list (line 111)
- new getallheaders_ (line 112)
- new http_response_code (line 113)
- new urlencode (line 114)
- new rawurlencode (line 115)
- new urldecode (line 116)
- new rawurldecode (line 117)
- new parse_url (line 118)
- new dirname (line 119)
- new basename (line 120)
- new realpath (line 121)
- new file_get_contents (line 122)
- new getenv_ (line 123)
- new putenv_ (line 124)
- new extract_ (line 125)
- new compact_ (line 126)
- new scandir (line 127)
- new glob_ (line 128)
- new time (line 129)
- new date (line 130)
- new gmdate (line 131)
- new web_int (line 114)
- new web_string (line 115)
- new web_bool (line 116)
- new urlencode (line 117)
- new rawurlencode (line 118)
- new urldecode (line 119)
- new rawurldecode (line 120)
- new parse_url (line 121)
- new dirname (line 122)
- new basename (line 123)
- new realpath (line 124)
- new file_get_contents (line 125)
- new getenv_ (line 126)
- new putenv_ (line 127)
- new extract_ (line 128)
- new compact_ (line 129)
- new scandir (line 130)
- new glob_ (line 131)
- new time (line 132)
- new date (line 133)
- new gmdate (line 134)
- 2 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `ext/standard/VmDate.php`
Expand Down Expand Up @@ -1126,6 +1149,21 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
**Warnings** (review for bootstrap subset):
- 3 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `ext/standard/web_bool.php`

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

### `ext/standard/web_int.php`

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

### `ext/standard/web_string.php`

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

### `ext/types/Module.php`

**Warnings** (review for bootstrap subset):
Expand Down Expand Up @@ -1824,6 +1862,12 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
**Warnings** (review for bootstrap subset):
- 20 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Web/Params.php`

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

### `lib/Web/ProjectManifest.php`

**Warnings** (review for bootstrap subset):
Expand Down
10 changes: 8 additions & 2 deletions docs/bootstrap-profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"ext/standard/JitStripTags.php",
"ext/standard/JitStrpos.php",
"ext/standard/JitUrlencode.php",
"ext/standard/JitWebParams.php",
"ext/standard/Module.php",
"ext/standard/VmDate.php",
"ext/standard/VmExit.php",
Expand All @@ -73,6 +74,7 @@
"ext/standard/array_keys.php",
"ext/standard/array_merge.php",
"ext/standard/array_pop.php",
"ext/standard/array_product.php",
"ext/standard/array_push.php",
"ext/standard/array_reverse.php",
"ext/standard/array_search.php",
Expand Down Expand Up @@ -173,6 +175,9 @@
"ext/standard/urldecode.php",
"ext/standard/urlencode.php",
"ext/standard/var_dump.php",
"ext/standard/web_bool.php",
"ext/standard/web_int.php",
"ext/standard/web_string.php",
"ext/types/Module.php",
"ext/types/is_type.php",
"ext/types/mb_strlen.php",
Expand Down Expand Up @@ -260,6 +265,7 @@
"lib/VM/ScriptExit.php",
"lib/VM/Variable.php",
"lib/Web/DevServer.php",
"lib/Web/Params.php",
"lib/Web/ProjectManifest.php",
"lib/Web/ResponseContext.php",
"lib/Web/Superglobals.php",
Expand All @@ -274,9 +280,9 @@
"test/bootstrap-aot/echo_hello.php"
],
"totals": {
"inventory_files": 240,
"inventory_files": 246,
"excluded": 2,
"eligible": 238,
"eligible": 244,
"aot_lint_targets": 2
}
}
3 changes: 3 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,8 @@ Auto-generated by `script/capability-matrix.php`. Do not edit by hand.
| `ucwords` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `urldecode` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `urlencode` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `web_bool` | yes | no | no | standard | not implemented for JIT in this compiler build; use web_int($source, $key, 0) for AOT or VM |
| `web_int` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
| `web_string` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |

_AOT uses the same LLVM builtin path as JIT unless noted otherwise._
193 changes: 193 additions & 0 deletions ext/standard/JitWebParams.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\standard;

use PHPCompiler\JIT\BasicBlockHelper;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPLLVM\Builder;
use PHPLLVM\Value;

/**
* JIT lowering for web_int(), web_string(), web_bool() (issue #157).
*/
final class JitWebParams
{
public static function webInt(Context $context, JITVariable ...$args): Value
{
$argc = count($args);
if ($argc < 3 || $argc > 5) {
throw new \LogicException('web_int() requires three to five arguments in this compiler build');
}
if (JITVariable::TYPE_HASHTABLE !== $args[0]->type
|| JITVariable::TYPE_STRING !== $args[1]->type
|| JITVariable::TYPE_NATIVE_LONG !== $args[2]->type) {
throw new \LogicException(
'web_int() requires (array, string key, int default) in this compiler build'
);
}
if ($argc >= 4 && JITVariable::TYPE_NATIVE_LONG !== $args[3]->type) {
throw new \LogicException('web_int() min must be an integer in this compiler build');
}
if ($argc >= 5 && JITVariable::TYPE_NATIVE_LONG !== $args[4]->type) {
throw new \LogicException('web_int() max must be an integer in this compiler build');
}

$i64 = JitStringIndex::i64($context);
$defaultVal = $context->helper->loadValue($args[2]);

$missingBlock = BasicBlockHelper::append($context, 'web_int_missing');
$invalidBlock = BasicBlockHelper::append($context, 'web_int_invalid');
$numBlock = BasicBlockHelper::append($context, 'web_int_num');
$convertBlock = BasicBlockHelper::append($context, 'web_int_convert');
$doneBlock = BasicBlockHelper::append($context, 'web_int_done');

$exists = (new array_key_exists())->call($context, $args[1], $args[0]);
$context->builder->branchIf($exists, $convertBlock, $missingBlock);

$context->builder->positionAtEnd($missingBlock);
$context->builder->branch($doneBlock);

$context->builder->positionAtEnd($convertBlock);
$ht = $context->helper->loadValue($args[0]);
$key = $context->helper->loadValue($args[1]);
$boxed = $context->builder->call(
$context->lookupFunction('__hashtable__readStringKeyValue'),
$ht,
$key
);
$str = $context->builder->call(
$context->lookupFunction('__value__readString'),
$boxed
);
$strVar = new JITVariable($context, JITVariable::TYPE_STRING, JITVariable::KIND_VALUE, $str);
$isNum = (new is_numeric())->call($context, $strVar);
$context->builder->branchIf($isNum, $numBlock, $invalidBlock);

$context->builder->positionAtEnd($invalidBlock);
$context->builder->branch($doneBlock);

$context->builder->positionAtEnd($numBlock);
$parsed = self::stringToInt64($context, $str);
$result = $parsed;
if ($argc >= 4) {
$min = $context->helper->loadValue($args[3]);
$result = JitStringIndex::max($context, $result, $min);
}
if ($argc >= 5) {
$max = $context->helper->loadValue($args[4]);
$result = JitStringIndex::min($context, $result, $max);
}
$valueBlock = $context->builder->getInsertBlock();
$context->builder->branch($doneBlock);

$context->builder->positionAtEnd($doneBlock);
$phi = $context->builder->phi($i64);
$phi->addIncoming($defaultVal, $missingBlock);
$phi->addIncoming($defaultVal, $invalidBlock);
$phi->addIncoming($result, $valueBlock);

return $phi;
}

public static function webString(Context $context, JITVariable ...$args): Value
{
$argc = count($args);
if ($argc < 2 || $argc > 4) {
throw new \LogicException('web_string() requires two to four arguments in this compiler build');
}
if (JITVariable::TYPE_HASHTABLE !== $args[0]->type
|| JITVariable::TYPE_STRING !== $args[1]->type) {
throw new \LogicException(
'web_string() requires (array, string key) in this compiler build'
);
}
$empty = $context->builder->call(
$context->lookupFunction('__string__alloc'),
JitStringIndex::zero($context)
);
if (2 === $argc) {
$defaultStr = $empty;
} elseif (JITVariable::TYPE_STRING !== $args[2]->type) {
throw new \LogicException('web_string() default must be a string in this compiler build');
} else {
$defaultStr = $context->helper->loadValue($args[2]);
}
if ($argc >= 4 && JITVariable::TYPE_NATIVE_LONG !== $args[3]->type) {
throw new \LogicException('web_string() maxLen must be an integer in this compiler build');
}

$missingBlock = BasicBlockHelper::append($context, 'web_str_missing');
$trimBlock = BasicBlockHelper::append($context, 'web_str_trim');
$doneBlock = BasicBlockHelper::append($context, 'web_str_done');

$exists = (new array_key_exists())->call($context, $args[1], $args[0]);
$context->builder->branchIf($exists, $trimBlock, $missingBlock);

$context->builder->positionAtEnd($missingBlock);
$context->builder->branch($doneBlock);

$context->builder->positionAtEnd($trimBlock);
$ht = $context->helper->loadValue($args[0]);
$key = $context->helper->loadValue($args[1]);
$boxed = $context->builder->call(
$context->lookupFunction('__hashtable__readStringKeyValue'),
$ht,
$key
);
$raw = $context->builder->call(
$context->lookupFunction('__value__readString'),
$boxed
);
$rawVar = new JITVariable($context, JITVariable::TYPE_STRING, JITVariable::KIND_VALUE, $raw);
$trimmed = (new string_trim())->call($context, $rawVar);
$final = $trimmed;
if ($argc >= 4) {
$lenArg = $context->helper->loadValue($args[3]);
$zero = JitStringIndex::zero($context);
$trimVar = new JITVariable($context, JITVariable::TYPE_STRING, JITVariable::KIND_VALUE, $trimmed);
$offsetVar = new JITVariable(
$context,
JITVariable::TYPE_NATIVE_LONG,
JITVariable::KIND_VALUE,
$zero
);
$lenVar = new JITVariable(
$context,
JITVariable::TYPE_NATIVE_LONG,
JITVariable::KIND_VALUE,
$lenArg
);
$final = (new substr())->call($context, $trimVar, $offsetVar, $lenVar);
}
$valueBlock = $context->builder->getInsertBlock();
$context->builder->branch($doneBlock);

$context->builder->positionAtEnd($doneBlock);
$phi = $context->builder->phi($defaultStr->typeOf());
$phi->addIncoming($defaultStr, $missingBlock);
$phi->addIncoming($final, $valueBlock);

return $phi;
}

private static function stringToInt64(Context $context, Value $strPtr): Value
{
$structName = $strPtr->typeOf()->getElementType()->getName();
$map = $context->structFieldMap[$structName];
$charPtr = $context->builder->structGep($strPtr, $map['value']);
$endPtrSlot = $context->builder->alloca(
$context->getTypeFromString('int8*'),
1,
'web_int_end'
);
$nullEnd = $context->getTypeFromString('int8*')->constNull();
$context->builder->store($nullEnd, $endPtrSlot);
$dbl = $context->builder->call($context->lookupFunction('strtod'), $charPtr, $endPtrSlot);
$i64 = JitStringIndex::i64($context);

return $context->builder->fpToSi($dbl, $i64);
}
}
Loading
Loading