#25. バイトコードの生成

バイトコード生成は、CPython が構造化構文を実行可能な仮想マシン命令に変換する段階です。

パーサーは AST を構築します。

シンボル テーブルはスコープの動作を決定します。

次に、コンパイラは、Python セマンティクスを実装するバイトコード命令を発行します。

このソースの場合:python id="bjlwm8" def add(a, b): return a + b CPython は次のようなバイトコードを生成します。text id="plhxq4" LOAD_FAST a LOAD_FAST b BINARY_OP + RETURN_VALUE 正確な命令名と形式は Python のバージョンによって異なりますが、モデルは安定しています。```text id="ecx81v" bytecode is a low-level instruction stream executed by the CPython virtual machine


バイトコードの生成は、AST の構築とスコープ分析の後に行われます。```text id="5v2o9v"
source
    
tokenization
    
parsing
    
AST
    
symbol table
    
bytecode generation
    
code object
    
evaluation loop
```コンパイラは AST を実行し、命令とメタデータを出力します。

出力はコード オブジェクトの一部になります。

## 25.2 バイトコードが表すもの

バイトコードは CPython の仮想命令セットです。

マシンコードではありません。

ソースコードではありません。

これは、CPython インタープリター用に設計された中間実行言語です。

ソース例:```python id="mwhlwe"
x = 1 + 2
```考えられるバイトコード:```text id="cv4c36"
LOAD_CONST 3
STORE_NAME x
LOAD_CONST None
RETURN_VALUE
```インタプリタは後でこれらの命令を 1 つずつ実行します。

コンパイラの仕事は次のとおりです。```text id="wlk7tq"
preserve Python semantics
emit correct stack behavior
emit correct control flow
emit correct scope access
record source metadata
```## 25.3 バイトコードはスタックベースです

CPython バイトコードはスタック マシンを使用します。

ほとんどの命令は、評価スタックから値をプッシュまたはポップします。

例:```python id="qggm8k"
a + b
```編集:```text id="jlwm0j"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```スタックの進化:```text id="4tq4zg"
LOAD_FAST a
    stack: a

LOAD_FAST b
    stack: a, b

BINARY_OP +
    pop a and b
    push result
    stack: result
```コンパイラはスタック効果の一貫性を保つ必要があります。

すべてのバイトコード パスは有効なスタック状態を維持する必要があります。

## 25.4 式のコンパイル

式は値を生成します。

例:```python id="nl8bg0"
x + y * z
```AST は優先順位を維持します。```text id="0u6c93"
x + (y * z)
```バイトコード生成はその構造に従います。

概念的なバイトコード:```text id="jlwm0m"
LOAD_FAST x
LOAD_FAST y
LOAD_FAST z
BINARY_OP *
BINARY_OP +
```スタックの進化:```text id="f2zv3g"
x
x, y
x, y, z
x, temp
result
```コンパイラは部分式を再帰的にコンパイルします。

## 25.5 ステートメントのコンパイル

通常、ステートメントは副作用命令を発行します。

例:```python id="rj0nwu"
x = value
```編集:```text id="zyulq0"
compile expression value
store into target x
```考えられるバイトコード:```text id="jlwm0n"
LOAD_FAST value
STORE_FAST x
```例:```python id="9hq2o6"
return x
```編集:```text id="jlwm0o"
LOAD_FAST x
RETURN_VALUE
```コンパイラは次のものを区別します。```text id="n0c2jr"
expressions producing values
statements performing actions
```## 25.6 定数のロード

定数はからロードされます`co_consts`

例:```python id="s7hy3e"
x = 123
```バイトコード:```text id="jlwm0p"
LOAD_CONST 123
STORE_NAME x
```コンパイラは定数を`co_consts`そしてインデックス参照を発行します。

概念的には:```text id="j49sul"
co_consts:
    0: 123

instruction:
    LOAD_CONST 0
```インタプリタは実行中にインデックスを解決します。

## 25.7 ローカルのロード

高速ローカルはインデックス付きスロットを使用します。

例:```python id="bhz0x7"
def f(a, b):
    return a + b
```バイトコード:```text id="jlwm0q"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
RETURN_VALUE
```コンパイラが使用するのは、`LOAD_FAST`シンボル分析が分類されているため`a`そして`b`ローカル変数として。

高速ローカルは辞書検索を回避します。

## 25.8 グローバルのロード

グローバル検索と組み込み検索では異なる命令が使用されます。

例:```python id="y3afqz"
x = 10

def f():
    return x
```内部`f`:

```text id="jlwm0r"
LOAD_GLOBAL x
RETURN_VALUE
```インタプリタは以下をチェックします。```text id="l1ajyz"
function globals
then builtins
```コンパイラが選択するのは、`LOAD_GLOBAL`シンボルテーブル情報に基づきます。

## 25.9 クロージャ変数のロード

クロージャ変数は逆参照操作を使用します。

例:```python id="cv9ngd"
def outer():
    x = 1

    def inner():
        return x
```内部`inner`:

```text id="jlwm0s"
LOAD_DEREF x
RETURN_VALUE
```コンパイラは逆参照バイトコードを出力します。`x`囲んでいるスコープからキャプチャされた自由変数です。

クロージャ バイトコードは、通常のローカル スロットではなくセル オブジェクトにアクセスします。

## 25.10 割り当てターゲット

代入ターゲットは、通常の式とは異なる方法でコンパイルされます。

例:```python id="mav3fq"
x = 1
```バイトコード:```text id="jlwm0t"
LOAD_CONST 1
STORE_NAME x
```ただし、属性の割り当ては次のようになります。```python id="y7g47q"
obj.value = 1
```バイトコードの形状:```text id="jlwm0u"
LOAD_FAST obj
LOAD_CONST 1
STORE_ATTR value
```添字の割り当て:```python id="oxtk8t"
items[i] = value
```バイトコードの形状:```text id="jlwm0v"
LOAD_FAST items
LOAD_FAST i
LOAD_FAST value
STORE_SUBSCR
```ターゲットのコンパイルは AST コンテキストに依存します。

## 25.11 削除

削除には専用の命令を使用します。

例:```python id="d8j5gc"
del x
```考えられるバイトコード:```text id="jlwm0w"
DELETE_FAST x
```例:```python id="b4d5gn"
del obj.attr
```考えられるバイトコード:```text id="jlwm0x"
LOAD_FAST obj
DELETE_ATTR attr
```削除は割り当てではありません`None`。ターゲット タイプに応じてバインディングまたはオブジェクト エントリを削除します。

## 25.12 関数呼び出し

関数呼び出しにより複数の命令が生成されます。

例:```python id="a3j0ut"
f(x, y)
```概念的なバイトコード:```text id="jlwm0y"
LOAD_NAME f
LOAD_FAST x
LOAD_FAST y
CALL 2
POP_TOP
```コンパイラは次のことを行う必要があります。```text id="e8qjkl"
compile callable expression
compile positional arguments
compile keyword arguments
emit call instruction
handle stack layout
```メソッド呼び出しでは、特殊なバイトコード形式が使用される場合があります。

例:```python id="s4m8cq"
obj.run()
```可能な形状:```text id="jlwm0z"
LOAD_FAST obj
LOAD_METHOD run
CALL 0
```最新の CPython バージョンには、追加の特殊化と呼び出しに関するインライン キャッシュ動作が含まれています。

## 25.13 二項演算

算術演算と二項演算は演算命令を発行します。

例:```python id="gt4c4x"
a + b
```バイトコード:```text id="jlwm10"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```他の例:

| |操作 |
| ---------- | --------------- |
|`a - b`|引き算 |
|`a * b`|乗算 |
|`a / b`|部門 |
|`a // b`|フロア区分 |
|`a % b`|モジュロ |
|`a ** b`|パワー |
|`a @ b`|行列の乗算 |
|`a << b`|左シフト |
|`a & b`|ビット単位で |

コンパイラは操作命令を発行します。ランタイム型のディスパッチは後で行われます。

例:```python id="x8vwdk"
1 + 2
"a" + "b"
```どちらも同様にコンパイルされますが、ランタイム オブジェクトの動作は異なります。

## 25.14 比較

比較では比較演算が行われます。

例:```python id="e98twa"
a < b
```考えられるバイトコード:```text id="jlwm11"
LOAD_FAST a
LOAD_FAST b
COMPARE_OP <
```連鎖比較には、より複雑な制御フローが必要です。

例:```python id="pxo93r"
a < b < c
```これは評価する必要があります`b`一度。

概念的な編集:```text id="jlwm12"
LOAD_FAST a
LOAD_FAST b
COMPARE_OP <
conditional jump if false
LOAD_FAST b
LOAD_FAST c
COMPARE_OP <
```コンパイラは、Python の連鎖比較セマンティクスを保持します。

## 25.15 ブール演算

ブール演算はショートサーキットします。

例:```python id="dd8v9m"
a and b
```コンパイルパターン:```text id="jlwm13"
evaluate a
jump if false
evaluate b

b必要な場合にのみ実行されます。

同様に:python id="a3pgh7" a or b 評価するb場合のみaは誤りです。

短絡動作は、通常の関数呼び出しではなく、ジャンプによって実装されます。

25.16 条件式

例:python id="r74d92" x if cond else y コンパイルパターン:```text id="jlwm14" evaluate cond jump to else branch if false evaluate x jump to end evaluate y


## 25.17`if`ステートメント

:```python id="q70zfx"
if cond:
    a()
else:
    b()
```コンパイルパターン:```text id="jlwm15"
compile condition
jump to else if false
compile a()
jump to end
compile b()
end
```考えられるバイトコードの形状:```text id="jlwm16"
LOAD_NAME cond
POP_JUMP_IF_FALSE else_label

LOAD_NAME a
CALL
POP_TOP
JUMP_FORWARD end_label

else_label:
LOAD_NAME b
CALL
POP_TOP

end_label:
```コンパイラは、最終アセンブリの前に内部でラベルとジャンプ ターゲットを管理します。

##25.18`while`ループ

:```python id="0wdg4v"
while cond:
    work()
```コンパイルパターン:```text id="jlwm17"
loop_start:
    evaluate cond
    jump to end if false
    compile body
    jump to loop_start
loop_end:
```ループでは、次のブロック スタックの追跡が必要です。```text id="jlwm18"
break
continue
exception cleanup
```##25.19`for`ループ

:```python id="uk10dk"
for item in items:
    work(item)
```コンパイルパターン:```text id="jlwm19"
load iterable
get iterator

loop_start:
    get next item
    jump to end on StopIteration
    store item
    compile body
    jump to loop_start

loop_end:
```コンパイラは反復子プロトコルのバイトコードを出力します。

ニシキヘビ`for`ループはインデックス駆動ではなく反復子駆動です。

##25.20`break`そして`continue`例:```python id="h5f6ov"
while True:
    if stop:
        break

    continue

breakループの出口にジャンプします。continueループ継続ポイントにジャンプします。

コンパイラは、ネストされたループが正しく動作するようにループ コンテキスト構造を維持します。

例:python id="r7bz0d" for x in xs: for y in ys: break 内側のbreak内側のループのみを終了します。

25.21 例外処理

例外処理には構造化された制御フローのメタデータが必要です。

例:python id="12cm6v" try: risky() except ValueError: recover() finally: cleanup() 編集責任:```text id="jlwm1a" protected instruction ranges exception handler targets finally cleanup reraising behavior stack restoration


コンパイラは次のように記録します。```text id="jlwm1b"
instruction range
handler entry
handler type
stack depth information
```このメタデータにより、例外が発生したときにインタープリターがハンドラーに正しくジャンプできるようになります。

##25.22`with`ステートメント

:```python id="wq6o7z"
with open(path) as f:
    data = f.read()
```コンパイルパターン:```text id="jlwm1c"
evaluate context manager
call __enter__
store result
execute body
ensure __exit__ runs
handle exceptions correctly
```コンパイラはクリーンアップ ロジックを発行し、`__exit__`例外が発生しても実行されます。`with`コンパイルは例外処理機構と密接に関係しています。

## 25.23 関数の定義

関数定義は 2 段階でコンパイルされます。

例:```python id="8k2d07"
def f(x):
    return x + 1
```ステージ 1:```text id="jlwm1d"
compile function body into nested code object
```ステージ 2:```text id="jlwm1e"
emit runtime instructions creating function object
```概念的なバイトコード:```text id="jlwm1f"
LOAD_CONST <code object f>
MAKE_FUNCTION
STORE_NAME f
```本体自体は、ネストされたコード オブジェクト内のバイトコードになります。

## 25.24 クロージャ

:```python id="b4pshk"
def outer():
    x = 1

    def inner():
        return x
```編集責任:```text id="jlwm1g"
create closure cell for x
compile inner with free variable access
pass closure tuple during function creation
```内部の可能なバイトコード形状`outer`:

```text id="jlwm1h"
MAKE_CELL x
LOAD_CONST 1
STORE_DEREF x

LOAD_CLOSURE x
BUILD_TUPLE 1
LOAD_CONST <code object inner>
MAKE_FUNCTION closure
```内部`inner`:

```text id="jlwm1i"
LOAD_DEREF x
RETURN_VALUE
```## 25.25 クラス定義

:```python id="1br4s3"
class C:
    x = 1
```クラス本体はネストされたコード オブジェクトになります。

コンパイルパターン:```text id="jlwm1j"
compile class body code object
emit runtime class construction logic
bind resulting class object
```クラス本体は、独自の名前空間を持つミニモジュールのように実行されます。

メソッドは、クラス本体コード オブジェクト内のネストされた関数定義になります。

## 25.26 内包表記

内包表記はネストされたスコープにコンパイルされます。

例:```python id="9w8n9x"
[x * x for x in xs]
```編集責任:```text id="jlwm1k"
create nested comprehension code object
iterate input iterable
bind local iteration variable
append results
return constructed container
```コンパイラーは別個の実行スコープ機構を作成するため、内包変数は外部スコープにリークしません。

## 25.27 ジェネレーター

ジェネレータ関数は一時停止ポイントを使用します。

例:```python id="i8gtwx"
def gen():
    yield 1
    yield 2
```編集責任:```text id="jlwm1l"
mark code object as generator
emit yield instructions
preserve resumable execution state
```考えられるバイトコードの形状:```text id="jlwm1m"
LOAD_CONST 1
YIELD_VALUE

LOAD_CONST 2
YIELD_VALUE

LOAD_CONST None
RETURN_VALUE
```フレームはサスペンション全体で状態を保持する必要があります。

## 25.28 コルーチンと`await`例:```python id="oqf8so"
async def fetch():
    return await client.get()
```編集責任:```text id="jlwm1n"
mark coroutine flags
emit await handling
preserve suspension semantics
```コンパイラは、通常の同期呼び出しではなく、コルーチンのスケジューリング動作用のバイトコードを生成します。

## 25.29 インポート

:```python id="3k7d9u"
import os
```コンパイルパターン:```text id="jlwm1o"
IMPORT_NAME os
STORE_NAME os
```例:```python id="38ef9k"
from math import sin
```コンパイルパターン:```text id="jlwm1p"
IMPORT_NAME math
IMPORT_FROM sin
STORE_NAME sin
```コンパイラはインポート操作を発行します。実際のモジュールの読み込みは実行時に行われます。

## 25.30 アサーション

:```python id="g2m5qn"
assert x > 0
```コンパイルパターン:```text id="jlwm1q"
evaluate condition
jump if true
raise AssertionError
```最適化モード中 (`python -O`)assert ステートメントは完全に省略できます。

これはコンパイラレベルの変換です。

## 25.31 ソースの場所とラインテーブル

コンパイラは、バイトコードとソース位置の間のマッピングを記録します。

これらのマッピングは以下をサポートします。```text id="jlwm1r"
tracebacks
debuggers
profilers
coverage tools
stepping
error reporting
```各命令範囲は次のものに対応します。```text id="jlwm1s"
line number
column offset
end line
end column
```コード オブジェクトには、圧縮されたマッピング テーブルが格納されます。

## 25.32 スタックサイズの計算

コンパイラは最大スタック深さを計算します。

例:```python id="7qdzg3"
a + b * c
```考えられるスタックの進化:```text id="jlwm1t"
a
a, b
a, b, c
a, temp
result
```最大深さ: 3

これは次のようになります`co_stacksize`

フレームは、この値に基づいて十分なスタック領域を割り当てます。

## 25.33 基本ブロック

内部的には、コンパイラーは多くの場合、命令を基本ブロックにグループ化します。

基本ブロックは、次のような一連の命令です。```text id="jlwm1u"
single entry
single exit
no internal jumps
```例:```python id="w2g2q7"
if cond:
    a()
b()
```考えられるブロック構造:```text id="jlwm1v"
block 1:
    evaluate cond
    conditional jump

block 2:
    a()
    jump

block 3:
    b()
```基本ブロックにより、制御フロー解析とジャンプ解決が簡素化されます。

## 25.34 ジャンプの解像度

コンパイラは最初にシンボリック ラベルを生成します。

実際の命令オフセットは、後のアセンブリで解決されます。

概念的なプロセス:```text id="jlwm1w"
emit instructions
emit labels
calculate instruction offsets
replace labels with offsets
insert extended arguments if needed
recalculate offsets if sizes changed
```ジャンプ解像度は、コンパイルが多段階である理由の 1 つです。

## 25.35 インラインキャッシュと特殊化のサポート

最新の CPython バイトコードは適応型特殊化をサポートしています。

コンパイラは、命令に関連付けられたキャッシュ エントリを発行する場合があります。

特殊化の恩恵を受ける操作の例:```text id="jlwm1x"
attribute access
global lookup
binary operations
calls
method dispatch
```初期バイトコードは汎用です。

ランタイムの特殊化により、後で動作が最適化された高速パスに置き換えられる場合があります。

コンパイラは、この適応を可能にする命令レイアウトを準備します。

## 25.36 バイトコード検査

使用する`dis`生成されたバイトコードを検査します。

例:```python id="jlwm1y"
import dis

def f(a, b):
    return a + b

dis.dis(f)
```便利な検査機能には次のものがあります。```text id="jlwm1z"
dis.dis
dis.Bytecode
dis.get_instructions
```バイトコード検査は次の場合に不可欠です。```text id="jlwm20"
compiler debugging
performance analysis
tooling
education
reverse engineering Python behavior
```## 25.37 バージョンの機密性

CPython のバージョン間でバイトコードが変更されます。

変更には次のものが含まれる場合があります。```text id="jlwm21"
new opcodes
removed opcodes
opcode renaming
instruction format changes
specialization changes
exception handling changes
line table changes
```バージョン固有のサポートが存在しない限り、ツールはバージョン間で正確なバイトコード レイアウトに依存することを避ける必要があります。

可能な場合は、生のバイトコードを手動で解析するのではなく、パブリック API を使用してください。

## 25.38 重要な CPython ソース領域

重要なファイルには次のものが含まれます。```text id="jlwm22"
Python/compile.c
Python/flowgraph.c
Python/assemble.c
Python/bytecodes.c
Include/opcode_ids.h
Lib/dis.py
Lib/opcode.py
```概念的な役割:

|エリア |役割 |
| ------------- | -------------------------------------- |
|`compile.c`| AST トラバーサルと命令の発行 |
|`flowgraph.c`|制御フロー グラフの処理 |
|`assemble.c`|バイトコードアセンブリとジャンプ解像度 |
|`bytecodes.c`|オペコードの定義 |
|`opcode.py`|オペコードメタデータ |
|`dis.py`|バイトコード検査 |

## 25.39 最小限のメンタルモデル

このモデルを使用します。```text id="jlwm23"
The compiler walks the AST.
Expressions emit stack-based instructions.
Statements emit side-effect and control-flow instructions.
Constants, names, and locals become indexed table references.
Control flow becomes jumps and exception metadata.
Functions, classes, comprehensions, and generators create nested code objects.
The final instruction stream becomes part of a code object executed by the CPython virtual machine.
```バイトコードの生成は、Python 構文が実行可能な仮想マシン操作になる段階です。