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
4 changes: 3 additions & 1 deletion ext/standard/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ public function getFunctions(): array
new floor(),
new round(),
new sqrt(),
new pow(),
new intval(),
new floatval(),
new boolval(),
new gettype(),
new strval(),
new int_min(),
new int_max(),
new intdiv(),
Expand All @@ -50,7 +52,7 @@ public function jitInit(JIT\Context $context): void
$context->registerFunction('strcmp', $fn);
}
$double = $context->getTypeFromString('double');
foreach (['ceil', 'floor', 'round', 'sqrt'] as $name) {
foreach (['ceil', 'floor', 'round', 'sqrt', 'pow'] as $name) {
try {
$context->lookupFunction($name);
} catch (\Throwable $e) {
Expand Down
78 changes: 78 additions & 0 deletions ext/standard/pow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\ext\standard;

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/**
* pow() with two integer or float arguments (subset of PHP standard library).
*/
final class pow extends Internal
{
public function execute(Frame $frame): void
{
if (2 !== count($frame->calledArgs)) {
throw new \LogicException('pow() requires exactly two arguments');
}
$base = $frame->calledArgs[0]->resolveIndirect();
$exp = $frame->calledArgs[1]->resolveIndirect();
if (null === $frame->returnVar) {
return;
}
$frame->returnVar->float(\pow(self::toFloat($base), self::toFloat($exp)));
}

public Context $context;

public function call(Context $context, JITVariable ...$args): Value
{
$this->context = $context;
if (2 !== count($args)) {
throw new \LogicException('pow() requires exactly two arguments');
}
$double = $context->getTypeFromString('double');
$base = self::toJitDouble($context, $args[0], $double);
$exp = self::toJitDouble($context, $args[1], $double);
$fn = $context->lookupFunction('pow');

return $context->builder->call($fn, $base, $exp);
}

private static function toFloat(Variable $v): float
{
if (Variable::TYPE_INTEGER === $v->type) {
return (float) $v->toInt();
}
if (Variable::TYPE_FLOAT === $v->type) {
return $v->toFloat();
}
throw new \LogicException('pow() only supports integers and floats in this compiler build');
}

private static function toJitDouble(Context $context, JITVariable $arg, $double): Value
{
$v = $context->helper->loadValue($arg);
switch ($arg->type) {
case JITVariable::TYPE_NATIVE_LONG:
return $context->builder->siToFp($v, $double);
case JITVariable::TYPE_NATIVE_DOUBLE:
return $v;
default:
throw new \LogicException('pow() only supports integers and floats in this compiler build');
}
}
}
117 changes: 117 additions & 0 deletions ext/standard/strval.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\ext\standard;

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/**
* strval() for scalar values supported by this compiler (subset of PHP).
*/
final class strval extends Internal
{
public function execute(Frame $frame): void
{
if (1 !== count($frame->calledArgs)) {
throw new \LogicException('strval() requires exactly one argument');
}
$v = $frame->calledArgs[0]->resolveIndirect();
if (null === $frame->returnVar) {
return;
}
if (Variable::TYPE_NULL === $v->type) {
$frame->returnVar->string('');

return;
}
$frame->returnVar->string($v->toString());
}

public Context $context;

public function call(Context $context, JITVariable ...$args): Value
{
$this->context = $context;
if (1 !== count($args)) {
throw new \LogicException('strval() requires exactly one argument');
}
switch ($args[0]->type) {
case JITVariable::TYPE_STRING:
return $context->helper->loadValue($args[0]);
case JITVariable::TYPE_NULL:
return $context->builder->load($context->constantStringFromString(''));
case JITVariable::TYPE_NATIVE_BOOL:
return $this->boolToString($context, $context->helper->loadValue($args[0]));
case JITVariable::TYPE_NATIVE_LONG:
return $this->formatToString($context, $context->helper->loadValue($args[0]), '%lld');
case JITVariable::TYPE_NATIVE_DOUBLE:
return $this->formatToString($context, $context->helper->loadValue($args[0]), '%G');
default:
throw new \LogicException('strval() does not support this value type in this compiler build');
}
}

private function boolToString(Context $context, Value $bool): Value
{
$prev = $context->builder->getInsertBlock();
$trueBlock = $prev->insertBasicBlock('strval_true');
$falseBlock = $prev->insertBasicBlock('strval_false');
$endBlock = $falseBlock->insertBasicBlock('strval_bool_end');
$context->builder->branchIf($bool, $trueBlock, $falseBlock);
$context->builder->positionAtEnd($trueBlock);
$trueStr = $context->builder->load($context->constantStringFromString('1'));
$context->builder->branch($endBlock);
$context->builder->positionAtEnd($falseBlock);
$falseStr = $context->builder->load($context->constantStringFromString(''));
$context->builder->branch($endBlock);
$context->builder->positionAtEnd($endBlock);
$phi = $context->builder->phi($trueStr->typeOf());
$phi->addIncoming($trueStr, $trueBlock);
$phi->addIncoming($falseStr, $falseBlock);

return $phi;
}

private function formatToString(Context $context, Value $value, string $format): Value
{
$sizeT = $context->getTypeFromString('size_t');
$charPtr = $context->getTypeFromString('char*');
$i64 = $context->getTypeFromString('int64');
$bufSize = $sizeT->constInt(64, false);
$buf = $context->builder->call($context->lookupFunction('__mm__malloc'), $bufSize);
$bufChar = $context->builder->pointerCast($buf, $charPtr);
$fmt = $context->builder->pointerCast(
$context->constantFromString($format),
$charPtr
);
$written = $context->builder->call(
$context->lookupFunction('snprintf'),
$bufChar,
$bufSize,
$fmt,
$value
);
$len = $context->builder->zExt($written, $i64);
$str = $context->builder->call(
$context->lookupFunction('__string__init'),
$len,
$bufChar
);
$context->builder->call($context->lookupFunction('__mm__free'), $buf);

return $str;
}
}
11 changes: 11 additions & 0 deletions test/compliance/cases/stdlib/pow.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
stdlib pow() for integers and floats
--FILE--
<?php
echo pow(2, 3), "\n";
echo pow(10, 2), "\n";
echo pow(9, 0.5), "\n";
--EXPECT--
8
100
3
9 changes: 9 additions & 0 deletions test/compliance/cases/stdlib/pow_int.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
stdlib pow() with integer base and exponent
--FILE--
<?php
echo intval(pow(3, 2)), "\n";
echo intval(pow(2, 4)), "\n";
--EXPECT--
9
16
18 changes: 18 additions & 0 deletions test/compliance/cases/stdlib/strval.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
stdlib strval() for scalar values
--FILE--
<?php
echo strval(42), "\n";
echo strval(-3), "\n";
echo strval(1.5), "\n";
echo strval(true), "\n";
echo strval(false), "\n";
echo strval(null), "\n";
echo strval('hi'), "\n";
--EXPECT--
42
-3
1.5
1

hi
6 changes: 5 additions & 1 deletion test/real/cases/stdlib_casts.phpt
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
--TEST--
Integration: floatval, boolval, and gettype for null
Integration: floatval, boolval, gettype, and strval for null
--FILE--
<?php
echo floatval(intval(2.9)), "\n";
echo boolval(floatval(1)) ? 'y' : 'n', "\n";
echo gettype(null), "\n";
echo is_bool(boolval(true)) ? 'y' : 'n', "\n";
echo strval(null), "\n";
echo strlen(strval(pow(2, 2))), "\n";
--EXPECT--
2
y
NULL
y

1
13 changes: 13 additions & 0 deletions test/real/cases/stdlib_strval_pow.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Integration: strval, pow, and strlen
--FILE--
<?php
echo strlen(strval(pow(2, 3))), "\n";
echo strval(pow(5, 2)), "\n";
echo strval(intval(pow(9, 0.5))), "\n";
echo boolval(strlen(strval(null))) ? 'y' : 'n', "\n";
--EXPECT--
1
25
3
n