22. Trình biên dịch
22. Trình biên dịch
Trình biên dịch chuyển thông tin về bảng ký hiệu và AST thành đối tượng mã thực thi.
Các giai đoạn trước trả lời các câu hỏi về cấu trúc.```text id="lfk0j9" tokenizer: What lexical units are in the source?
parser: What syntax tree do these tokens form?
symbol table:
What scope does each name belong to?
Trình biên dịch trả lời các câu hỏi thực thi.text id="ju2gsy"
Which bytecode instructions should be emitted?
Which constants belong in co_consts?
Which names belong in co_names?
Which local variables belong in co_varnames?
Where should jumps go?
How large can the evaluation stack grow?
Which exception table entries are needed?
Which nested code objects must be created?
```Đầu ra là mộtcodesự vật. Đối tượng mã đó có thể được thực thi bởi vòng đánh giá CPython.
22.1 Vị trí trong Đường dẫn biên dịch
Trình biên dịch nằm sau khi phân tích cú pháp và phân tích phạm vi.```text id="9mc7xw" source ↓ tokenization ↓ parsing ↓ AST ↓ symbol table ↓ compiler passes ↓ code object ↓ interpreter execution
Ở cấp độ Python,`compile()`chức năng hiển thị giai đoạn này:```python id="2sweqj"
src = "x = 1 + 2\n"
code = compile(src, "<input>", "exec")
print(code)
```Bạn có thể kiểm tra kết quả với`dis`:
```python id="24znqy"
import dis
code = compile("x = 1 + 2\n", "<input>", "exec")
dis.dis(code)
```## 22.2 Đầu vào chính của Trình biên dịch
Trình biên dịch sử dụng hai sản phẩm chính từ các giai đoạn trước.
| Đầu vào | Vai trò |
| ------------- | ------------------------------------------------------------------ |
| AST | Mô tả các câu lệnh, biểu thức, toán tử và vị trí nguồn |
| Bảng ký hiệu | Mô tả phân loại phạm vi cho tên |
AST nói:```text id="iqtzc2"
there is an assignment
target is Name("x")
value is BinOp(Constant(1), Add, Constant(2))
```Bảng ký hiệu cho biết:```text id="2kd8cu"
x is local/global in this scope
```Chúng cùng nhau cho phép tạo mã byte.
Ví dụ: trình biên dịch chọn giữa:```text id="1lrzcy"
STORE_FAST
STORE_NAME
STORE_GLOBAL
STORE_DEREF
```tùy thuộc vào thông tin phạm vi.
## 22.3 Đầu ra của trình biên dịch: Đối tượng mã
Trình biên dịch phát ra các đối tượng mã.
Đối tượng mã chứa mã byte và siêu dữ liệu thực thi.
Các trường quan trọng bao gồm:```text id="6p1zp9"
co_code bytecode bytes
co_consts constants
co_names global and attribute names
co_varnames local variable names
co_freevars free variable names
co_cellvars cell variable names
co_filename source filename
co_name function or module name
co_qualname qualified name
co_firstlineno first source line
co_flags execution flags
co_stacksize required value stack size
```Ví dụ:```python id="z2p4ol"
def add(a, b):
return a + b
code = add.__code__
print(code.co_name)
print(code.co_varnames)
print(code.co_consts)
print(code.co_names)
print(code.co_stacksize)
```Đối tượng mã là dữ liệu thực thi bất biến. Đối tượng hàm bao bọc một đối tượng mã bằng ngữ cảnh thời gian chạy chẳng hạn như toàn cục, mặc định, chú thích và ô đóng.
## 22.4 Biên dịch là đệ quy
Các hàm lồng nhau, lambda, mức hiểu, lớp và biểu thức trình tạo tạo ra các đối tượng mã lồng nhau.
Ví dụ:```python id="nxf9b9"
def outer(x):
def inner(y):
return x + y
return inner
```Đối tượng mã mô-đun chứa một đối tượng mã chức năng cho`outer`.
các`outer`đối tượng mã chứa một đối tượng mã chức năng cho`inner`.
Về mặt khái niệm:```text id="g7e4jo"
module code object
constants:
code object for outer
constants:
code object for inner
```Trong thời gian chạy, thực thi`def outer`tạo một đối tượng hàm từ`outer`đối tượng mã. Đang thực hiện`def inner`bên trong`outer`tạo một đối tượng hàm khác, với các ô đóng khi cần.
## 22.5 Biên soạn câu lệnh
Các câu lệnh thường phát ra mã byte để thực hiện các hành động.
Ví dụ:```python id="f8r2kh"
x = 1
```Mẫu biên dịch:```text id="vbnwbg"
compile right-hand expression
store result into target
```Mã byte được đơn giản hóa:```text id="ozi020"
LOAD_CONST 1
STORE_NAME x
```Ví dụ:```python id="jflq5g"
return x
```Mẫu biên dịch:```text id="j5nviu"
compile return expression
emit RETURN_VALUE
```Mã byte được đơn giản hóa:```text id="5k1c90"
LOAD_FAST x
RETURN_VALUE
```Mã byte chính xác thay đổi theo phiên bản Python, nhưng ý tưởng cấu trúc vẫn ổn định.
## 22.6 Biên dịch biểu thức
Biểu thức phát ra mã byte để lại một giá trị trên ngăn xếp đánh giá.
Ví dụ:```python id="thj50j"
a + b
```Mẫu biên dịch:```text id="f51gg5"
load a
load b
apply binary operation
```Mã byte được đơn giản hóa:```text id="esgtwo"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```Một câu lệnh biểu thức thường loại bỏ kết quả của nó:```python id="77sckz"
f()
```Mẫu biên dịch:```text id="u882gm"
load function
call function
pop result
```Mã byte được đơn giản hóa:```text id="fo9egq"
LOAD_NAME f
CALL
POP_TOP
```Trình biên dịch duy trì kỷ luật ngăn xếp. Mọi biểu thức đều có hiệu ứng ngăn xếp đã biết.
## 22.7 Biên soạn tên
Mã byte tên phụ thuộc vào phân loại phạm vi.
Ví dụ:```python id="7v96c6"
def f(a):
return a
alà một biến cục bộ:text id="spceye" LOAD_FAST a Ví dụ:```python id="o6h1if"
x = 1
def f(): return x
`x`mang tính toàn cầu từ bên trong`f`:
```text id="xmpdg1"
LOAD_GLOBAL x
```Ví dụ:```python id="yptzi2"
def outer():
x = 1
def inner():
return x
xlà một biến đóng từ bên tronginner:
LOAD_DEREF x
```Đây là lý do tại sao việc phân tích bảng ký hiệu diễn ra trước việc phát mã byte.
## 22.8 Biên dịch mục tiêu
Mục tiêu gán được biên dịch khác với biểu thức giá trị.
Ví dụ:```python id="xpt040"
x = value
```Mục tiêu hoạt động:```text id="jfu667"
STORE_FAST or STORE_NAME or STORE_GLOBAL or STORE_DEREF
```Ví dụ:```python id="rtgk3q"
obj.attr = value
```Mục tiêu hoạt động:```text id="r72bwa"
compile obj
compile value
STORE_ATTR attr
```Ví dụ:```python id="s5jn7u"
items[i] = value
```Mục tiêu hoạt động:```text id="yyzjb4"
compile items
compile i
compile value
STORE_SUBSCR
```Hình dạng AST giống nhau có thể biên dịch khác nhau tùy theo ngữ cảnh.
So sánh:```python id="i5tdmi"
obj.attr
```được sử dụng làm giá trị:```text id="14wnr4"
LOAD_ATTR attr
```Và:```python id="c46iv2"
obj.attr = 1
```được sử dụng làm mục tiêu:```text id="7uswmv"
STORE_ATTR attr
```Bối cảnh AST như`Load`, `Store`, Và`Del`thúc đẩy sự khác biệt này.
## 22.9 Biên soạn luồng điều khiển
Luồng điều khiển yêu cầu nhảy và nhãn.
Ví dụ:```python id="5u72qc"
if x:
a = 1
else:
a = 2
```Mẫu biên dịch:```text id="c7me6c"
compile test x
jump to else block if false
compile body
jump to end
else label:
compile else body
end label:
continue
```Hình dạng mã byte được đơn giản hóa:```text id="ukb3dq"
LOAD_NAME x
POP_JUMP_IF_FALSE else_label
LOAD_CONST 1
STORE_NAME a
JUMP_FORWARD end_label
else_label:
LOAD_CONST 2
STORE_NAME a
end_label:
```Trình biên dịch đầu tiên phát ra các bước nhảy tượng trưng hoặc các nhãn bên trong. Việc lắp ráp sau này phân giải chúng thành các độ lệch byte cụ thể hoặc độ lệch lệnh.
## 22.10 Vòng lặp
Vòng lặp yêu cầu nhãn vào, nhãn thoát và đôi khi là mục tiêu tiếp tục.
Ví dụ:```python id="28xqrv"
while x:
work()
```Mẫu biên dịch:```text id="aq977s"
loop_start:
compile test
jump to loop_end if false
compile body
jump to loop_start
loop_end:
```Ví dụ:```python id="u0a0kq"
for item in items:
work(item)
```Mẫu biên dịch:```text id="fs8o3k"
compile iterable
get iterator
loop_start:
get next item or jump to loop_end
store item
compile body
jump to loop_start
loop_end:
breaknhảy đến lối ra vòng lặp.continuenhảy tới điểm tiếp tục vòng lặp.
Các vòng lặp lồng nhau yêu cầu ngăn xếp khối trình biên dịch nênbreakVàcontinuenhắm mục tiêu vòng lặp kèm theo chính xác.
22.11 Xử lý ngoại lệ
Quá trình biên dịch xử lý ngoại lệ phức tạp hơn vì nó cần siêu dữ liệu luồng điều khiển.
Ví dụ:python id="zbie3d" try: risky() except ValueError: recover() finally: cleanup() Trình biên dịch phải tạo mã byte cho:```text id="7fd19n"
normal execution
exception matching
handler entry
handler cleanup
finally execution
reraising when needed
Về mặt khái niệm:```text id="9wg6kk"
protected bytecode range:
risky()
handler:
if exception matches ValueError:
recover()
finally:
cleanup()
```Trình biên dịch phải bảo toàn ngữ nghĩa ngoại lệ chính xác của Python, bao gồm`else`, `finally`, `raise`, xâu chuỗi ngoại lệ và dọn dẹp các biến xử lý ngoại lệ.
## 22.12 Biên dịch định nghĩa hàm
Một định nghĩa hàm biên dịch thành hai lớp.
Ví dụ:```python id="9423bh"
def add(a, b):
return a + b
```Lớp 1: biên dịch phần thân hàm thành một đối tượng mã lồng nhau.```text id="8pamlr"
code object for add:
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
RETURN_VALUE
```Lớp 2: biên dịch câu lệnh bên ngoài tạo đối tượng hàm.```text id="ndgkj2"
LOAD_CONST <code object add>
MAKE_FUNCTION
STORE_NAME add
```Sự khác biệt này là trung tâm.
Thân hàm không chạy khi trình biên dịch nhìn thấy`def`. Trong thời gian chạy, việc thực thi`def`câu lệnh tạo một đối tượng hàm và liên kết nó với tên hàm.
## 22.13 Mặc định, Chú thích và Trang trí
Các định nghĩa hàm có thể bao gồm các giá trị mặc định, chú thích và trang trí.
Ví dụ:```python id="06culw"
@trace
def f(x: int, y=1) -> int:
return x + y
```Quá trình biên dịch phải xử lý:```text id="st02n8"
default argument value y=1
parameter annotation x: int
return annotation -> int
function body code object
function object creation
decorator application
name binding
```Thứ tự thời gian chạy khái niệm:```text id="e9t5yf"
evaluate decorator expressions
evaluate default values
evaluate annotations according to current rules
create function object
apply decorators from bottom to top
bind final function object to name
```Trình biên dịch phát ra mã byte thực hiện thứ tự này.
## 22.14 Biên soạn định nghĩa lớp
Một định nghĩa lớp cũng biên dịch theo lớp.
Ví dụ:```python id="c7bkv3"
class C(Base):
x = 1
def f(self):
return self.x
```Thân lớp trở thành một đối tượng mã.
Khi chạy:```text id="s2rsuu"
evaluate base classes
create class namespace
execute class body code object in that namespace
call metaclass machinery
bind resulting class object to name
```Trình biên dịch phát ra mã byte gọi giao thức xây dựng lớp bên trong.
Thân lớp là mã thực thi được. Nó có thể chứa các câu lệnh tùy ý:```python id="we8528"
class C:
print("building class")
x = compute()
```Những câu lệnh đó chạy khi định nghĩa lớp được thực thi.
## 22.15 Biên dịch đóng cửa
Việc đóng cửa yêu cầu công việc phối hợp giữa phân tích bảng ký hiệu và tạo mã byte.
Ví dụ:```python id="ovqkwa"
def outer():
x = 1
def inner():
return x
return inner
```Trình biên dịch phải:```text id="ovv4rt"
create a cell for x in outer
compile inner with x as a free variable
create inner function with closure tuple
load x through closure access in inner
```Hình dạng đơn giản bên trong`outer`:
```text id="3irffr"
MAKE_CELL x
LOAD_CONST 1
STORE_DEREF x
LOAD_CLOSURE x
BUILD_TUPLE 1
LOAD_CONST <code object inner>
MAKE_FUNCTION closure
STORE_FAST inner
LOAD_FAST inner
RETURN_VALUE
```Bên trong`inner`:
```text id="31h0a6"
LOAD_DEREF x
RETURN_VALUE
```Các hướng dẫn chính xác khác nhau tùy theo phiên bản, nhưng cơ chế đóng vẫn giữ nguyên: các biến được ghi lại nằm trong các ô.
## 22.16 Biên soạn hiểu
Sự hiểu biết được biên dịch thành các đối tượng mã lồng nhau.
Ví dụ:```python id="5w7ihk"
values = [x * x for x in range(5)]
```Về mặt khái niệm, CPython tạo mã tương tự như một hàm lồng nhau ngầm cho phần nội dung hiểu.
Mã bên ngoài đánh giá lần lặp và gọi mã hiểu. Mã hiểu sẽ lặp lại và xây dựng danh sách kết quả.
Điều này giải thích tại sao các biến hiểu không rò rỉ vào phạm vi xung quanh.```python id="in5tuc"
x = 100
values = [x for x in range(3)]
print(x)
```Bên ngoài`x`còn lại`100`.
Sự hiểu biết`x`là cục bộ của đối tượng mã hiểu.
## 22.17 Biên dịch trình tạo
Các hàm tạo được biên dịch khác với các hàm thông thường.
Ví dụ:```python id="esmh0i"
def gen():
yield 1
yield 2
```Trình biên dịch đánh dấu đối tượng mã bằng các cờ của trình tạo.
Khi được gọi,`gen()`không thực thi cơ thể ngay lập tức. Nó trả về một đối tượng máy phát điện.
Phần thân thực thi khi trình tạo được nâng cao.
Quá trình biên dịch phải phát ra điểm năng suất:```text id="xk7fji"
LOAD_CONST 1
YIELD_VALUE
resume point
LOAD_CONST 2
YIELD_VALUE
resume point
RETURN_VALUE
```Khung máy phát điện có thể tạm dừng và tiếp tục. Cờ đối tượng mã cho bộ thực thi biết cách xây dựng và thực thi nó.
## 22.18 Biên dịch Coroutine và Async
Các hàm không đồng bộ tạo ra các đối tượng mã coroutine.
Ví dụ:```python id="5d36kx"
async def fetch():
result = await client.get()
return result
```Trình biên dịch đánh dấu đối tượng mã là mã coroutine.
Nó phát ra máy móc chờ đợi xung quanh các biểu thức có thể chờ đợi.
Về mặt khái niệm:```text id="g68r60"
call client.get()
obtain awaitable
suspend until awaitable completes
store result
return result
```Chức năng không đồng bộ, trình tạo không đồng bộ,`async for`, Và`async with`tất cả đều yêu cầu các mẫu mã byte và cờ mã đặc biệt.
## 22.19 Biên dịch khớp mẫu
Khớp mẫu có logic biên dịch chuyên dụng.
Ví dụ:```python id="jz9doq"
match value:
case 0:
result = "zero"
case [x, y]:
result = x + y
```Trình biên dịch phải phát ra mã tới:```text id="1gk5a7"
evaluate subject once
try first pattern
jump to body if matched
try next pattern if failed
bind captured names only on successful match
evaluate guards when present
skip remaining cases after match
```Việc khớp mẫu không hề bình thường`if`cú pháp cũng như phép gán thông thường. Nó có các quy tắc phù hợp và ràng buộc riêng.
## 22.20 Xử lý liên tục
Trình biên dịch lưu trữ các hằng số trong`co_consts`.
Ví dụ:```python id="j0h9t8"
x = 123
y = "hello"
```Các hằng số đối tượng mã có thể bao gồm:```text id="4721fz"
None
123
"hello"
nested code objects
tuples of constants
frozensets used by optimized membership tests
```Thanh tra:```python id="qrm79o"
code = compile("x = 123\ny = 'hello'\n", "<input>", "exec")
print(code.co_consts)
```Các hằng số được tham chiếu theo chỉ mục từ các hướng dẫn mã byte.
## 22.21 Tên và bảng biến
Trình biên dịch xây dựng một số bảng tên.
| Trường đối tượng mã | Ý nghĩa |
| ----------------- | ----------------------------------------------- |
|`co_names`| Tên được sử dụng cho toàn cầu, thuộc tính và nhập khẩu |
|`co_varnames`| Tên biến cục bộ nhanh |
|`co_cellvars`| Người dân địa phương bị bắt bởi phạm vi lồng nhau |
|`co_freevars`| Các biến được ghi lại từ phạm vi kèm theo |
Ví dụ:```python id="m1cvy7"
x = 1
def f(a):
b = len(a)
return x + b
```Bên trong`f`:
```text id="8bgw2w"
co_varnames:
a, b
co_names:
len, x
```Nếu việc đóng cửa có liên quan,`co_cellvars`Và`co_freevars`xuất hiện.
## 22.22 Tính toán kích thước ngăn xếp
Mã byte CPython sử dụng ngăn xếp giá trị.
Trình biên dịch phải tính toán độ sâu ngăn xếp tối đa cần thiết cho một đối tượng mã.
Ví dụ:```python id="kt5lcp"
x = a + b * c
```Có thể phát triển ngăn xếp:```text id="q0x4gy"
LOAD a stack: a
LOAD b stack: a, b
LOAD c stack: a, b, c
MULTIPLY stack: a, result
ADD stack: result
STORE x stack:
```Độ sâu ngăn xếp tối đa: 3.
Đối tượng mã lưu trữ cái này dưới dạng`co_stacksize`.
Trình thông dịch sử dụng nó để xác định kích thước lưu trữ khung.
## 22.23 Bảng số dòng và vị trí
Trình biên dịch ghi lại ánh xạ giữa mã byte và vị trí nguồn.
Các ánh xạ này hỗ trợ:```text id="srmq4m"
tracebacks
debuggers
profilers
coverage tools
stepping behavior
error locations
```Ví dụ:```python id="st4v12"
def f():
x = 1
y = 2
return x + y
```Mỗi phạm vi mã byte có thể được liên kết với thông tin về dòng và cột nguồn.
CPython hiện đại theo dõi thông tin vị trí chính xác hơn các phiên bản cũ hơn, giúp cải thiện khả năng theo dõi và gỡ lỗi.
## 22.24 hội
Trình biên dịch thường phát ra biểu diễn lệnh trung gian trước bytecode cuối cùng.
Hội giải quyết:```text id="ic8am2"
labels
jump targets
instruction offsets
extended arguments
line tables
exception tables
final bytecode layout
```Điều này là cần thiết vì độ lệch nhảy phụ thuộc vào kích thước lệnh và kích thước lệnh có thể thay đổi khi độ lệch yêu cầu đối số mở rộng.
Về mặt khái niệm:```text id="nxzhpt"
emit symbolic instructions
compute instruction offsets
resolve jumps
insert extended arguments if needed
recompute if sizes changed
build final bytecode bytes
build metadata tables
create code object
```## 22.25 Tối ưu hóa AST
CPython thực hiện một số tối ưu hóa ở cấp độ AST hoặc trình biên dịch.
Ví dụ có thể bao gồm:```text id="45a56p"
constant folding
removing unreachable assert code under optimization
simplifying literal containers for membership tests
basic expression simplifications
```Ví dụ:```python id="hspw89"
x = 1 + 2
```có thể biên dịch như thể được viết:```python id="99zdo5"
x = 3
```Thanh tra:```python id="3ltxiz"
import dis
dis.dis(compile("x = 1 + 2\n", "<input>", "exec"))
```Tối ưu hóa là bảo thủ. CPython phải bảo toàn ngữ nghĩa của Python, bao gồm các tác dụng phụ, ngoại lệ và thứ tự đánh giá.
## 22.26 Thứ tự đánh giá
Trình biên dịch phải giữ nguyên thứ tự đánh giá của Python.
Ví dụ:```python id="0rat4c"
result = f() + g()
f()phải thực hiện trướcg().
Ví dụ:```python id="3cgy5k" obj.attr = value()
Ví dụ:```python id="9uqpy4"
d[key()] = value()
```Trình biên dịch không thể tự do sắp xếp lại các thao tác chỉ vì một thứ tự khác có thể nhanh hơn.
Ngữ nghĩa động của Python biến thứ tự đánh giá thành một phần của hành vi có thể quan sát được.
## 22.27 Xử lý lỗi trong quá trình biên dịch
Một số lỗi được phát hiện trong quá trình biên dịch, không phân tích cú pháp.
Ví dụ:```python id="uxlk51"
return 1
```ở cấp độ mô-đun.```python id="nlyacf"
break
```bên ngoài một vòng lặp.```python id="kggjx7"
continue
```bên ngoài một vòng lặp.
Trình phân tích cú pháp có thể xây dựng các nút cho các câu lệnh này. Trình biên dịch từ chối chúng khi ngữ cảnh khiến chúng trở nên bất hợp pháp.
Điều này có nghĩa là AST có dạng cú pháp có thể vẫn không biên dịch được.
## 22.28 API công khai để biên dịch
Python trưng bày quá trình biên dịch thông qua`compile()`.
Ví dụ:```python id="njqphs"
code = compile("x = 1\n", "<input>", "exec")
exec(code)
```Chế độ:
| Chế độ | Ý nghĩa |
| -------- | -------------------------------------- |
|`exec`| Biên dịch một mô-đun hoặc chuỗi câu lệnh |
|`eval`| Biên dịch một biểu thức |
|`single`| Biên dịch đầu vào tương tác |
Ví dụ:```python id="s246ja"
compile("x = 1", "<input>", "exec")
compile("1 + 2", "<input>", "eval")
compile("print(1)", "<input>", "single")
compile()cũng có thể biên dịch AST:```python id="mcvj8n"
import ast
tree = ast.parse("x = 1\n")
code = compile(tree, "
Các tệp quan trọng liên quan đến trình biên dịch bao gồm:```text id="jwz8yn"
Python/compile.c
Python/symtable.c
Python/ast.c
Python/ast_opt.c
Python/flowgraph.c
Python/assemble.c
Include/cpython/code.h
Lib/dis.py
Lib/test/test_compile.py
Lib/test/test_dis.py
```Vai trò khái niệm:
| Khu vực | Vai trò |
| -------------- | ---------------------------------- |
|`symtable.c`| Phân loại phạm vi |
|`compile.c`| Biên dịch AST theo hướng dẫn |
|`flowgraph.c`| Xử lý biểu đồ luồng điều khiển |
|`assemble.c`| Tập hợp mã byte cuối cùng |
|`code.h`| Định nghĩa đối tượng mã |
|`dis.py`| Kiểm tra mã byte |
| kiểm tra trình biên dịch | Bảo vệ hành vi và hồi quy |
Tổ chức tệp chính xác có thể thay đổi giữa các phiên bản, nhưng quy trình khái niệm vẫn ổn định.
## 22.30 Mô hình tinh thần tối thiểu
Sử dụng mô hình này:```text id="ti9m3l"
The AST gives syntax.
The symbol table gives scope.
The compiler walks the AST and emits bytecode instructions.
Nested executable constructs produce nested code objects.
Control flow becomes jumps and exception tables.
Names become local, global, or closure bytecode.
Constants and names are stored in code object tables.
Assembly resolves labels, jumps, stack size, and metadata.
The result is an immutable code object ready for execution.
```Các trình biên dịch là nơi CPython biến cú pháp và phạm vi thành các hướng dẫn thực thi.