TỪ NGÔN NGỮ LẬP TRÌNH ĐẾN MÃ MÁY (phần 2)


Sau khi đã hoàn thành tiền xử lý, chúng ta sẽ sang bước thứ hai: Biên dịch.

Về cơ bản quá trình biên dịch nhận đầu vào là file mã nguồn, phân tích nó và xuất ra file nhị phân chứa mã thực thi. Quá trình này sẽ chia làm 3 bước chính:

👉 Phân tích từ vựng (lexical analysis)Trình biên dịch sẽ đọc file mã nguồn, từng ký tự một, sau đó phân tích các ký tự rời rạc đó thành các từ vựng trong bộ từ vựng của ngôn ngữ tương ứng.

Xét ví dụ sau:

int x = myfunc(x) * 10

Ví dụ trên sẽ được phân tích thành các từ vựng (ta có thể dùng từ tiếng Anh là token): int, x, =, myfunc, (, x, ), *, 10.Trình phân tích từ vựng sử dụng các ký tự phân tách (khoảng trắng, tab, xuống dòng, dấu chấm phẩy… tùy ngôn ngữ) để tìm ra các từ vựng, nó cũng biết rằng chữ n và t nằm sau i phải được kết hợp thành keyword int, ký tự 1 và 0 phải được kết hợp thành một nhóm là 10 – và đây là một số nguyên, tương tự cho các token còn lại.

👉 Phân tích ngữ nghĩa (semantic analysis)Sau khi đã có danh sách các token, bước kế tiếp là phân tích chúng và tạo nên một cây ngữ nghĩa. Nhìn vào cấu trúc một chương trình, ta dễ dàng thấy chúng được tổ chức theo dạng cây:

– Một file chương trình là nút gốc, có các nút con là các function, hoặc các module, biến toàn cục, các lệnh import…

– Mỗi lệnh khai báo biến toàn cục sẽ có các nút con là kiểu dữ liệu, tên biến.- Mỗi hàm có các nút con là các kiểu trả về, tên hàm, danh sách tham số… Danh sách tham số lại bao gồm các nút con, mỗi nút là một tham số, rồi mỗi tham số lại có các nút là kiểu, tên, các tùy chọn khác…

– Mỗi câu lệnh bên trong hàm lại có thể có các nút con toán tử, các toán hạng, các lời gọi hàm, tham số…Cây này được gọi là cây ngữ nghĩa (tiếng Anh là abstract syntax tree, hay AST), quá trình phân tích từ danh sách các token thành cây này gọi là phân tích ngữ nghĩa, trong quá trình phân tích, trình dịch sẽ phát hiện ra các lỗi cú pháp nếu như thứ tự các token không đúng quy tắc.

Dựa vào cây này, người ta có thể tối ưu lại mã lệnh, giúp tạo ra các câu lệnh hiệu quả hơn.

Ví dụ:x = 7 + 10;Khi phân tích xong, nếu trình dịch phát hiện ra tất cả các toán hạng đều là hằng số, nó có thể tính ra luôn kết quả và thay thế công thức trên bằng một phép gán: x = 17; Những thành phần được khai báo nhưng không có bất kỳ tham chiếu nào trên cây cũng có thể được loại bỏ.

👉 Sinh mã:Khi đã có cây ngữ nghĩa, trình dịch sẽ tạo ra mã đích từ cây này. Tùy thuộc vào trình dịch mà quá trình sinh mã có thể rất khác nhau. Ví dụ như rất nhiều trình dịch C khi sinh mã sẽ tạo ra các file mã nguồn Assembly (mã nguồn này sau đó lại được dịch bởi trình dịch Assembly), một số khác có thể sinh ra mã máy, các trình dịch Java có thể tạo ra file .class chứa mã bytecode…Bản thân quá trình sinh mã này cũng có thể được tối ưu dựa trên kiến trúc đích. Ví dụ đối với các máy tính x32 – có word size là 32 bit (4 byte), các biến thường sẽ được đặt vào các vị trí có địa chỉ là bội số của 4. Nguyên nhân là vì bộ xử lý khi đọc/ghi dữ liệu sẽ luôn làm việc với word size của nó, giả sử bạn có 1 biến int32 được đặt ở địa chỉ 0, toàn bộ 4 byte của biến này có thể được đọc chỉ trong 1 lần truy xuất bộ nhớ, nhưng nếu nó được đặt ở các địa chỉ 1, CPU sẽ phải thực hiện 2 lần đọc: lần thứ nhất để lấy 3 byte đầu, lần thứ 2 để lấy 1 byte cuối cùng vốn nằm trong word kế tiếp.

(Chữ word ở đây chỉ đến 16bit với CPU x16, 32 với CPU x32, 64 với CPU x64…)Thông thường mỗi một file mã nguồn sẽ sinh ra một file đích, và không hẳn các file đích sẽ có thể chạy được ngay. Một chương trình có hàng trăm, thậm chí hàng ngàn file mã nguồn là bình thường. Với trường hợp của C, chúng sẽ được dịch thành các file Assembly (.a, .s hoặc .asm…) rồi sau đó lại được dịch tiếp sang mã máy. Các file mã máy này (ở bước này ta gọi là các file object (.obj)), cần phải được liên kết lại (link) để có thể tạo ra sản phẩm cuối cùng chạy được (sẽ nói đến trong bài sau).

Một trong những cuốn sách nổi tiếng nhất trong giới viết trình dịch là cuốn: Compilers Principles, Techniques, & Tools, hay còn gọi là bộ sách con rồng (vì trên bìa sách có hình con rồng), trước đây đã có bản dịch sang tiếng Việt, đây cũng là một trong những bộ sách yêu thích nhất của tôi từ thời còn sinh viên, nếu các bạn quan tâm có thể tìm đọc (https://www.amazon.com/Compilers-Principles-Techniques-Tools-2nd/dp/0321486811)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s