diff --git a/ext/standard/Module.php b/ext/standard/Module.php index f72da203c..7914e4a84 100755 --- a/ext/standard/Module.php +++ b/ext/standard/Module.php @@ -182,6 +182,13 @@ public function jitInit(JIT\Context $context): void $context->registerFunction('realpath', $fn); } $double = $context->getTypeFromString('double'); + try { + $context->lookupFunction('fabs'); + } catch (\Throwable $e) { + $ft = $context->context->functionType($double, false, $double); + $fn = $context->module->addFunction('fabs', $ft); + $context->registerFunction('fabs', $fn); + } foreach (['ceil', 'floor', 'round', 'sqrt', 'log', 'exp', 'sin', 'cos', 'tan', 'pow', 'fmod'] as $name) { try { $context->lookupFunction($name); diff --git a/ext/standard/VmNumberFormat.php b/ext/standard/VmNumberFormat.php new file mode 100644 index 000000000..37764619f --- /dev/null +++ b/ext/standard/VmNumberFormat.php @@ -0,0 +1,100 @@ + 0) { + $rounded = round($number, $decimals); + } else { + $rounded = round($number, 0); + } + + $intPart = (int) floor($rounded); + $fracPart = 0; + if ($decimals > 0) { + $fracPart = (int) round(($rounded - (float) $intPart) * $pow); + if ($fracPart >= $pow) { + ++$intPart; + $fracPart = 0; + } + } + + $intDigits = self::digitsFromInt($intPart); + $result = self::insertThousands($intDigits, $thousandsSeparator); + + if ($decimals > 0) { + $fracDigits = self::padLeft(self::digitsFromInt($fracPart), $decimals, '0'); + $result .= $decimalSeparator.$fracDigits; + } + + if ($negative) { + return '-'.$result; + } + + return $result; + } + + private static function digitsFromInt(int $value): string + { + if (0 === $value) { + return '0'; + } + $digits = ''; + while ($value > 0) { + $digits = \chr(48 + ($value % 10)).$digits; + $value = (int) ($value / 10); + } + + return $digits; + } + + private static function padLeft(string $digits, int $length, string $pad): string + { + while (VmString::byteLength($digits) < $length) { + $digits = $pad.$digits; + } + + return $digits; + } + + private static function insertThousands(string $digits, string $separator): string + { + $len = VmString::byteLength($digits); + if ($len <= 3 || '' === $separator) { + return $digits; + } + $firstGroup = $len % 3; + if (0 === $firstGroup) { + $firstGroup = 3; + } + $out = VmString::byteSlice($digits, 0, $firstGroup); + for ($i = $firstGroup; $i < $len; $i += 3) { + $out .= $separator.VmString::byteSlice($digits, $i, 3); + } + + return $out; + } +} diff --git a/ext/standard/number_format.php b/ext/standard/number_format.php index d1d7dbd49..73c6c3060 100644 --- a/ext/standard/number_format.php +++ b/ext/standard/number_format.php @@ -61,7 +61,12 @@ public function execute(Frame $frame): void } $thousandsSeparator = $thouVar->toString(); } - $frame->returnVar->string(\number_format($num, $decimals, $decimalSeparator, $thousandsSeparator)); + $frame->returnVar->string(VmNumberFormat::format( + $num, + $decimals, + $decimalSeparator, + $thousandsSeparator + )); } public Context $context; diff --git a/lib/JIT/ArrayBuiltinHelper.php b/lib/JIT/ArrayBuiltinHelper.php index 450adc134..9bbb16e51 100644 --- a/lib/JIT/ArrayBuiltinHelper.php +++ b/lib/JIT/ArrayBuiltinHelper.php @@ -306,7 +306,10 @@ public static function copyInto(Context $context, Value $dest, Value $src): void ); $context->builder->branch($head); + $merge = BasicBlockHelper::append($context, 'merge_copy_exit'); $context->builder->positionAtEnd($done); + $context->builder->branch($merge); + $context->builder->positionAtEnd($merge); } public static function inArray( @@ -318,6 +321,7 @@ public static function inArray( $ht = self::loadHashTable($context, $haystack); $sizeT = $context->getTypeFromString('size_t'); $zero = $sizeT->constInt(0, false); + $one = $sizeT->constInt(1, false); $idxSlot = $context->builder->alloca($sizeT, 1, 'in_array_idx'); $context->builder->store($zero, $idxSlot); $num = $context->builder->call( @@ -346,15 +350,27 @@ public static function inArray( $context->builder->positionAtEnd($body); $candidate = self::readListElement($context, $ht, $idx, $needle->type); $match = self::valuesEqual($context, $needle, $candidate, $strict); - $context->builder->branchIf($match, $foundBlock, $head); + $continueBlock = BasicBlockHelper::append($context, 'in_array_continue'); + $context->builder->branchIf($match, $foundBlock, $continueBlock); + + $context->builder->positionAtEnd($continueBlock); + $context->builder->store( + $context->builder->addNoSignedWrap($idx, $one), + $idxSlot + ); + $context->builder->branch($head); $context->builder->positionAtEnd($foundBlock); $context->builder->store($context->getTypeFromString('int1')->constInt(1, false), $foundSlot); $context->builder->branch($done); + $merge = BasicBlockHelper::append($context, 'in_array_merge'); $context->builder->positionAtEnd($done); + $result = $context->builder->load($foundSlot); + $context->builder->branch($merge); + $context->builder->positionAtEnd($merge); - return $context->builder->load($foundSlot); + return $result; } private static function listEntryAt(Context $context, Value $ht, Value $index): Value diff --git a/test/compliance/cases/stdlib/number_format_jit.phpt b/test/compliance/cases/stdlib/number_format_jit.phpt new file mode 100644 index 000000000..5d6fcc51b --- /dev/null +++ b/test/compliance/cases/stdlib/number_format_jit.phpt @@ -0,0 +1,11 @@ +--TEST-- +stdlib number_format() JIT +--FILE-- +