diff --git a/src/Php/Optimizer/FuncCallOptimizer.php b/src/Php/Optimizer/FuncCallOptimizer.php index 84ae9ae3..70471000 100644 --- a/src/Php/Optimizer/FuncCallOptimizer.php +++ b/src/Php/Optimizer/FuncCallOptimizer.php @@ -193,6 +193,40 @@ trait FuncCallOptimizer if ($name === 'get_class') { return $this->genGetClass($expr); } + if (count($expr->args) === 1) { + $arg = $expr->args[0]->value; + $type = $this->detectTypeOfExpr($arg); + // is_* compile-time elimination when SSA-narrowed + if ($name === 'is_int' && $type === self::TYPE_INT) { + return 'true'; + } + if ($name === 'is_float' && $type === self::TYPE_FLOAT) { + return 'true'; + } + if ($name === 'is_bool' && $type === self::TYPE_BOOL) { + return 'true'; + } + if ($name === 'is_null') { + return $this->parseIdentifier($arg) . '.isNull()'; + } + // Compile-time count() on literal arrays + if ($name === 'count' && $arg instanceof Node\Expr\Array_) { + $itemCount = count($arg->items); + return $itemCount . $this->getPlatform()->getIntegerLiteralSuffix(); + } + // Compile-time string operations on literals + if ($this->isScalarString($arg)) { + $val = $arg->value; + switch ($name) { + case 'strtoupper': + return $this->getLiteralString(strtoupper($val)); + case 'strtolower': + return $this->getLiteralString(strtolower($val)); + case 'trim': + return $this->getLiteralString(trim($val)); + } + } + } return false; } diff --git a/src/Php/Parser/BinaryOpTrait.php b/src/Php/Parser/BinaryOpTrait.php index 00dbe061..0ff4147c 100644 --- a/src/Php/Parser/BinaryOpTrait.php +++ b/src/Php/Parser/BinaryOpTrait.php @@ -230,9 +230,35 @@ trait BinaryOpTrait if ($right === 'nullptr') { return $left . '.isNull()'; } + if ($optimized = $this->optimizeIdenticalOp($expr->left, $expr->right, $left, $right)) { + return $optimized; + } return 'php::same(' . $left . ', ' . $right . ')'; } + /** + * Use compile-time type info to optimize === and !== . + * When both sides are the same narrowed primitive type, emit direct C++ == . + * When both are narrowed but different types, === is always false. + */ + private function optimizeIdenticalOp(NodeAbstract $astLeft, NodeAbstract $astRight, string $cppLeft, string $cppRight): ?string + { + $primitiveTypes = [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL]; + $leftType = $this->detectTypeOfExpr($astLeft); + $rightType = $this->detectTypeOfExpr($astRight); + + if ($leftType === null || $rightType === null) { + return null; + } + if (!in_array($leftType, $primitiveTypes, true) || !in_array($rightType, $primitiveTypes, true)) { + return null; + } + if ($leftType === $rightType) { + return $cppLeft . ' == ' . $cppRight; + } + return 'false'; + } + protected function parseBinaryOpLogicalAnd(Expr\BinaryOp\LogicalAnd|Expr\BinaryOp\BooleanAnd $expr): string { return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '&&'));