diff --git a/ext/standard/Module.php b/ext/standard/Module.php index 129bc3d838d..abcc2df1971 100755 --- a/ext/standard/Module.php +++ b/ext/standard/Module.php @@ -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(), @@ -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) { diff --git a/ext/standard/pow.php b/ext/standard/pow.php new file mode 100644 index 00000000000..f0d0a5585e1 --- /dev/null +++ b/ext/standard/pow.php @@ -0,0 +1,78 @@ +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'); + } + } +} diff --git a/ext/standard/strval.php b/ext/standard/strval.php new file mode 100644 index 00000000000..db613196713 --- /dev/null +++ b/ext/standard/strval.php @@ -0,0 +1,117 @@ +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; + } +} diff --git a/test/compliance/cases/stdlib/pow.phpt b/test/compliance/cases/stdlib/pow.phpt new file mode 100644 index 00000000000..1825f114eb5 --- /dev/null +++ b/test/compliance/cases/stdlib/pow.phpt @@ -0,0 +1,11 @@ +--TEST-- +stdlib pow() for integers and floats +--FILE-- +