mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-12 04:56:18 +00:00
132 lines
4.7 KiB
Markdown
132 lines
4.7 KiB
Markdown
# Pythonバイトコード命令
|
|
|
|
[](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/python/bytecode_instructions.md&commit_hash=5affa5065fbcc598b9d196b824a2bc49e8612bd9)
|
|
|
|
Python bytecodeの変数操作系の命令はnamei (name index)を通してアクセスされる。これは、Pythonの動的変数アクセス(evalなどを使い、文字列でアクセスできる)を実現するためである。
|
|
1命令は2byteで、命令、引数がlittle endianで格納されている。
|
|
引数を取らない命令も2byte使っている(引数部は0)。
|
|
|
|
* 3.11での変更: 命令は固定長ではなくなり、一部の命令が2バイトを超えることがある。余計に入ったバイト列は殆どの場合が0であり、その目的は不明だが、最適化オプションが入るのではないかと考えられる。判明している変則バイト長命令は以下の通り。
|
|
* `PRECALL` (4 byte)
|
|
* `CALL` (10 byte)
|
|
* `BINARY_OP` (4 byte)
|
|
* `STORE_ATTR` (10 byte)
|
|
* `COMPARE_OP` (6 byte)
|
|
* `LOAD_GLOBAL` (12 byte)
|
|
* `LOAD_ATTR` (10 byte)
|
|
* `BINARY_SUBSCR` (8 byte)
|
|
|
|
## STORE_NAME(namei)
|
|
|
|
```python
|
|
globals[namei] = stack.pop()
|
|
```
|
|
|
|
## LOAD_NAME(namei)
|
|
|
|
```python
|
|
stack.push(globals[namei])
|
|
```
|
|
|
|
トップレベルでしか呼び出されない。
|
|
|
|
## LOAD_GLOBAL(namei)
|
|
|
|
```python
|
|
stack.push(globals[namei])
|
|
```
|
|
|
|
トップレベルでSTORE_NAMEしたものを内側のスコープでLoadするためのものだが、トップレベルでの`namei`ならばあるスコープのコードオブジェクトでのnameiとも同じとは限らない(nameiではなくnameが同じ)
|
|
|
|
## LOAD_CONST(namei)
|
|
|
|
```python
|
|
stack.push(consts[namei])
|
|
```
|
|
|
|
定数テーブルにある定数をロードする。
|
|
現在(Python 3.9)のところ、CPythonではいちいちラムダ関数を"\<lambda\>"という名前でMAKE_FUNCTIONしている
|
|
|
|
```console
|
|
>>> dis.dis("[1,2,3].map(lambda x: x+1)")
|
|
1 0 LOAD_CONST 0 (1)
|
|
2 LOAD_CONST 1 (2)
|
|
4 LOAD_CONST 2 (3)
|
|
6 BUILD_LIST 3
|
|
8 LOAD_ATTR 0 (map)
|
|
10 LOAD_CONST 3 (<code object <lambda> at 0x7f272897fc90, file "<dis>", line 1>)
|
|
12 LOAD_CONST 4 ('<lambda>')
|
|
14 MAKE_FUNCTION 0
|
|
16 CALL_FUNCTION 1
|
|
18 RETURN_VALUE
|
|
```
|
|
|
|
## STORE_FAST(namei)
|
|
|
|
```python
|
|
fastlocals[namei] = stack.pop()
|
|
```
|
|
|
|
おそらくトップレベルにおけるSTORE_NAMEに対応する
|
|
参照のない(もしくは単一)変数がこれによって格納されると思われる
|
|
わざわざグローバル空間が独自の命令を持っているのは最適化のため?
|
|
|
|
## LOAD_FAST(namei)
|
|
|
|
```python
|
|
stack.push(fastlocals[namei])
|
|
```
|
|
|
|
fastlocalsはvarnames?
|
|
|
|
## LOAD_CLOSURE(namei)
|
|
|
|
```python
|
|
cell = freevars[namei]
|
|
stack.push(cell)
|
|
```
|
|
|
|
そのあとBUILD_TUPLEが呼ばれている
|
|
クロージャの中でしか呼び出されないし、cellvarsはクロージャの中での参照を格納するものと思われる
|
|
LOAD_DEREFと違ってcell(参照を詰めたコンテナ)ごとスタックにpushする
|
|
|
|
## STORE_DEREF(namei)
|
|
|
|
```python
|
|
cell = freevars[namei]
|
|
cell.set(stack.pop())
|
|
```
|
|
|
|
内側のスコープで参照のない変数はSTORE_FASTされるが、参照される変数はSTORE_DEREFされる
|
|
Pythonではこの命令内で参照カウントの増減がされる
|
|
|
|
## LOAD_DEREF(namei)
|
|
|
|
```python
|
|
cell = freevars[namei]
|
|
stack.push(cell.get())
|
|
```
|
|
|
|
## 名前リスト
|
|
|
|
### varnames
|
|
|
|
fast_localsに対応する、関数の内部変数の名前リスト
|
|
namesで同名の変数があっても、基本的に同じものではない(新しく作られ、そのスコープからは外の変数にアクセスできない)
|
|
つまり、スコープ内で定義された外部参照のない変数はvarnamesに入る
|
|
|
|
### names
|
|
|
|
globalsに対応
|
|
スコープ内で使われた外部定数(参照だけしている)の名前リスト(トップレベルでは普通の変数でもnamesに入る)
|
|
つまり、スコープ外で定義された定数はnamesに入る
|
|
|
|
## free variable
|
|
|
|
freevarsに対応
|
|
クロージャがキャプチャした変数。同じ関数インスタンス内においてstaticな振る舞いをする。
|
|
|
|
## cell variables
|
|
|
|
cellvarsに対応
|
|
関数内で内側のクロージャ関数にキャプチャされる変数。コピーが作られるので、元の変数はそのまま。
|