Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPのOpcodeを 読んでみよう

yasuaki640
October 08, 2023

PHPのOpcodeを 読んでみよう

yasuaki640

October 08, 2023
Tweet

More Decks by yasuaki640

Other Decks in Programming

Transcript

  1. Opcodeの扱われ方(概要) 1. PHPコードを解析し、AST(抽象構文木)を出力 2. ASTからOpcodeを生成 3. OpcodeをPHP VMが実行 <?php echo

    "Hello"; ECHO string("Hello") RETURN int(1) array( 0: Stmt_Echo( exprs: array( 0: Scalar_String( value: Hello ) ... 解析 生成(emit) VMに渡され実行
  2. Opcodeの扱われ方(OPcache使用時) - Opcodeが共有メモリにキャッシュされる - PHPコードの解析~ASTの生成を飛ばすため処理が速い!! <?php echo "Hello"; ECHO string("Hello")

    RETURN int(1) array( 0: Stmt_Echo( exprs: array( 0: Scalar_String( value: Hello ) ... 解析 生成(emit) VMに渡され実行 ここだけ実行
  3. Opcodeを読む <?php $a = 1; $b = 2; $c =

    $a + $b; echo $c; $_main: ; (lines=6, args=0, vars=3, tmps=4) ; (before optimizer) ; /workspace/experimental.php:1-6 ; return [] RANGE[0..0] 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002 T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1)
  4. Opcodeを読む $_main: ; (lines=6, args=0, vars=3, tmps=4) ; (before optimizer)

    ; /workspace/experimental.php:1-6 ; return [] RANGE[0..0] 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002 T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1) 実行時のメタ情報 ダンプされた Opcode
  5. Opcodeを読む 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002

    T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1) PHPスクリプト上の 実際の変数($a, $b) に値を代入
  6. Opcodeを読む 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002

    T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1) 加算を実行し、 VM上の一時変数(T5)に 値を代入
  7. Opcodeを読む 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002

    T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1) VM上の一時変数(T5)の値を PHPスクリプト上の 実際の変数($c)に代入
  8. Opcodeを読む 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002

    T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1) 加算結果が代入された 変数に対しechoを実行
  9. Opcodeを読む 0000 ASSIGN CV0($a) int(1) 0001 ASSIGN CV1($b) int(2) 0002

    T5 = ADD CV0($a) CV1($b) 0003 ASSIGN CV2($c) T5 0004 ECHO CV2($c) 0005 RETURN int(1) PHP側で 暗黙的に付与されるreturn ※詳細は略
  10. Opcodeのハンドラを読む <?php function welcome(string $target): string { $hi = 'Welcome

    to' . $target; return $hi; } echo welcome('PHP conference!'); $_main: (省略) 0000 INIT_FCALL 1 144 string("welcome") 0001 SEND_VAL string("PHP conference!") 1 0002 V0 = DO_UCALL 0003 ECHO V0 0004 RETURN int(1) welcome: (省略) 0000 CV0($target) = RECV 1 0001 T2 = CONCAT string("Welcome to") CV0($target) 0002 ASSIGN CV1($hi) T2 0003 VERIFY_RETURN_TYPE CV1($hi) 0004 RETURN CV1($hi) 0005 VERIFY_RETURN_TYPE 0006 RETURN null
  11. Opcodeのハンドラを読む welcome: (省略) 0000 CV0($target) = RECV 1 0001 T2

    = CONCAT string("Welcome to") CV0($target) 0002 ASSIGN CV1($hi) T2 0003 VERIFY_RETURN_TYPE CV1($hi) 0004 RETURN CV1($hi) 0005 VERIFY_RETURN_TYPE 0006 RETURN null 引数の型チェック?? (ドキュメントに説明がない)
  12. 参考にさせていただいた記事 - php-srcを読んでみよう - https://fortee.jp/phpcon-2022/proposal/1addf51d-6f72-4c96-9337-034ec6cc0643 - PHP による hello world

    入門 - https://tech.respect-pal.jp/php-helloworld - PHP 7 Virtual Machine - https://www.npopov.com/2017/04/14/PHP-7-Virtual-machine.html - How to dump and inspect PHP OPCodes - https://php.watch/articles/php-dump-opcodes - Static Optimization of PHP bytecode (PHPSC 2017) - https://www.slideshare.net/nikita_ppv/static-optimization-of-php-bytecode-phpsc-2017 - Zend Engine 2 オペコード (PHP公式マニュアル) - http://php.adamharvey.name/manual/ja/internals2.opcodes.php - OPcache (PHP公式マニュアル) - https://www.php.net/manual/en/book.opcache.php - Implementing a Range Operator into PHP - https://phpinternals.net/articles/implementing_a_range_operator_into_php - Using CLion with php-src - https://dev.to/ramsey/using-clion-with-php-src-4me0