Trình biên dịch

Làm phần mềm ít ai không biết đến cái gọi là trình biên dịch, tuy nhiên cũng đặc thù từng công việc mà mức độ hiểu biết về trình biên dịch có khác nhau.
Tôi làm về hệ thống nhúng (rất không muốn dùng từ này thay từ embedded trong tiếng Anh, nhưng cũng phải dùng cho đậm đà bản sắc dân tộc), là mảng công việc rất rất cần hiểu sâu sắc về trình biên dịch, nhưng đáng buồn là giờ tôi mới tìm hiểu, thôi thì chậm hơn là không.

Trong công việc phần mềm, thì trình biên dịch được hiểu ngắn gọn là phần mềm chuyển từ mã nguồn do lập trình viên viết ra, biến thành mã máy (trong trường hợp dotNET hay Java chắc phải hiểu khác chút).

Trình biên dịch thường là do các công ty phát triển môi trường phát triển (IDE) cho lập trình viên viết ra, nổi bật như Microsoft, với trường hợp này trình biên dịch tích hợp trong môi trường phát triển nên lập trình viên cũng không cần để ý quá nhiều. Bên cạnh đó các trình biên dịch mã nguồn mở cũng chả kém cạnh, mà đứng đầu là GCC.

Có thể tham khảo về các thể loại trình biện dịch trên Wiki, với trình biên dịch cho ngôn ngữ C thì chỗ này.

Trình biên dịch phải được phát triển rất cẩn thận, vì nếu trình biên dịch mà có lỗi thì ảnh hưởng rất lớn trong việc phát triển các phần mềm dùng trình biên dịch đó. Nếu xuất hiện tình huống lỗi phần mềm đang phát triển, mà nó lại do cái lỗi kia của trình biên dịch thì đúng là ác mộng, không khác gì việc vẽ đường thẳng bằng một cái thước cong. Tuy nhiên trên thực tế lỗi của trình biên dịch vẫn như sao trời mùa hạ, ví dụ danh sách lỗi dược báo cáo của GCC chẳng hạn, biết làm sao được nó cũng chỉ là phần mềm thôi mà. Cách đấy hơn 2 năm, tôi cũng đã từng ăn quả đắng khi trình biên dịch cho hệ thống firmware video encoder bị lỗi khi biên dịch phép tính 64bit, do không thể đợi lỗi này được xử lý ở trình biên dịch, mà phải đi vòng (workaround) bằng cách thay thế toàn bộ tính toán 64bit bằng một thư viện mới xử lý thông qua trình toán 32bit.

Công việc từ trước đến này tôi chỉ dùng GCC, ngoài việc hỗ trợ nhiều ngôn ngữ, nó còn hỗ trợ hầu hết các platform, thành ra tôi luôn nghĩ nó là “độc cô cầu bại” trong thế giới công việc nhúng. Nhưng ai có ngờ lời xưa đã chứng, đâu ra lại xuất hiện 1 thằng clang đã có chiều hướng đè bẹp GCC? Tôi chưa thử so sách xem chất lượng mã máy được biên dịch ra thế nào, còn về tốc độ biên dịch thì GCC hít khói. Nói về clang thì nó chỉ là mặt tiền (bước phân tích mã nguồn) của một thứ đang sợ hơn là LVMM, dự án này bắt đầu từ nghiên cứu của Chris Lattner. Cầu thủ này được Apple thuê hơn 10 năm, trong thời gian đó thì clang xuất hiện và thay thế GCC trong các môi trường phát triển của Apple. Sau khi không đá cho Apple nữa mà chuyển sang chơi ô tô với Elon Musk, nhưng chỉ đá được gần 6 tháng chắc thấy đá không vừa chân nên chuyển qua chơi AI với google.

Cưỡi ngựa xem hoa thế là đủ rồi, đã đến giờ “học đi đôi với hành”, để hiểu hơn về trình biên dịch thì cũng nên một lần trong đời thử viết 1 con compiler be bé xem nó ra cái gì. Để làm được cái “be bé” đó trước hết chắc phải đọc kỹ cái đống dưới.

BOOK.
[1] https://www.amazon.com/dp/0321486811/?tag=stackoverflow17-20
[2] https://www.amazon.com/dp/0471976970/?tag=stackoverflow17-20
[3] https://holub.com/goodies/compiler/compilerDesignInC.pdf

Other.
[1] https://www.quora.com/How-do-I-make-a-compiler-using-C-language
[2] https://holub.com/compiler/
[3] https://norasandler.com/2017/11/29/Write-a-Compiler.html
[4] http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf
[5] https://softwareengineering.stackexchange.com/questions/165543/how-to-write-a-very-basic-compiler
[6] https://www.codeproject.com/articles/30353/designing-a-compiler

Creative Commons License

Câu chuyện từ khóa Volatile

Về từ khóa volatile thì có rất nhiều bài giải thích rồi, ví dụ như đây, đây đây nữa, tôi không muốn nhai đi nhai lại nhiều nữa.

Tuy nhiên vấn đề các tác giả đề cập đến thường là việc giá trị “biến thay đổi bất thường”, tôi không hiểu ý bất thường ở đây cụ thể là gì, bất thường đối với ai, nhưng có vẻ như là bất thường theo nghĩa trình biên dịch đọc code.
Để chắc bắp, nên tham khảo chính xác đặc tả C (or C++) xem nó nói gì, nhưng sờ vào đặc tả mới thấy nó không dễ đọc như tôi vẫn tưởng 😦

An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3.

Có thời gian nữa thì chắc nên nghiền ngẫm thêm chút vì thuật ngữ nghe là lạ, nhưng có thể tạm hiểu là từ khóa volatile để chỉ ra rằng biến đó có thể được thay đổi theo cách không rõ, không nhận biết được (mk, tiếng việt mình ngu quá, dài dòng vậy chứ có khi dùng từ “bất thường” là hợp lý nhất), cho nên việc tham chiếu biến đó phải tuyệt đối tuân thử đúng qui tắc máy :-o. Kết hợp thêm các ví dụ tham khảo thì tôi đang tạm hiểu ý là những chỗ đã liên quan đến từ khoá volatile thì phải tuyệt đối làm theo đúng từng mệnh lệnh mà mã nguồn đã được viết ra.
Dùng Google tìm kiếm thì có thể tóm tắt vài tình huống dưới, mà biến bị thay đổi một cách bất thường đối với trình biên dịch, làm cho nó không nhận thức được việc thay đổi đó.
– Trong chương trình có xử lý ngắt, vì xử lý ngắt tạo tình huống đặc biệt.
– Xử lý song song đa luồng, các luồng có sử dụng biến chung.
– Vùng nhớ sử dụng cho IO map.
Chính vì trình biên dịch không có khả năng (?) nhận thức, nhận biết được toàn bộ những thay đổi liên quan đến biến có mang từ khoá volatile, do đó nó phải tuân thủ từng dòng code chứ không được suy đoán, mà mục đích thường là để tối ưu khi biên dịch mã nguồn.
Tôi thì vẫn nghĩ chỉ có trường hợp 3 do trình biên dịch không thể đủ thông tin nên mới có thể làm sai khi tiến hành tối ưu code, chứ trường hợp interrupt hay xử lý đa luồng, thì rõ ràng mọi thứ trình biên dịch đều có thể nhận thức được, tôi không cho rằng cái đó là biến được thay đổi một cách bất thường. Tuy nhiên vì chưa có hiểu biết gì về cách tối ưu của trình biên dịch nên tôi cũng chỉ đưa ra nhận định chủ quan.

Sau khi đọc bài viết chỗ này, tôi đã thử 1 sampe code để test với gcc 6.3.0, dùng signal interrupt từ bàn phím, nhưng tối ưu hóa đủ mọi option vẫn không bị lỗi. Sau khi được tác giả gợi ý dùng fake interrupt bằng timer thì lại đúng là bị lỗi nếu không dùng volatile thật.
Dù sao chăng nữa thì tôi vẫn giữ quan điểm volatile chỉ cần trường hợp map IO do tác động thực sự từ yếu tố bên ngoài mà trình biên dịch có mắt cũng chả thấy được, à hoặc có thể một trường hợp là cố tình dump code theo ý người viết, mà thường thấy là tạo delay  cái này thường thấy khi lập trình cho vi điều khiển, do thư viện không hỗ trợ hàm delay, điều này cũng làm cho trình biên dịch phán đoán là đoán mã đó thừa và thực hiện tối ưu, do nó không hiểu “ngu ý” của người viết. Còn lại với những trường hợp mà bản thân trình biên dịch có đầy đủ thông tin nhưng vẫn biên dịch lỗi nếu không có volatile thì tôi nghĩ có khi phải xem xét lại bản thân trình biên dịch.

Tóm lại từ khóa volatile dùng để nói cho trình biên dịch đấy là vùng cấm, code thế nào thì hãy làm đúng như thế, mày mới chỉ biết 1 chứ chưa biết 2, phần mày đang biên dịch chỉ là 1 phần nhỏ trong hệ thống lớn, nên đừng có khôn lỏi vào chỗ đó, đừng dựa vào nhận định chủ quan mà tiến hành thay đổi code để thực hiện tối ưu.

Câu chuyện đến đây có lẽ là xong rồi, tuy nhiên có lẽ  ta dễ thấy có gì đó hơi không tự nhiên lắm, do nó chỉ bị lỗi khi yêu cầu trình biên dịch thực hiện tối ưu, vậy nếu trình biên dịch “ngây thơ” bảo gì làm nấy thì có lẽ là ta chẳng cần từ khóa volatile làm gì. Mà về tính năng tối ưu trong trình biên dịch thì tôi nghĩ nó chỉ phát triển sau này. Lúc xây dựng C không rõ trình biên dịch mà cụ Dennis Ritchie dùng có những tính năng gì (tôi không rõ ông làm thế nào để xây dựng ra C nên chỉ phán đoán thôi, nhưng dù sao chắc cũng phải vừa xây dựng đặc tả thì cũng phải song song xây dựng 1 trình biên dịch để kiểm thử).
Kiểm ra lại đặc cả ngôn ngữ C phiên bản đầu tiên thì chưa có thật, nó chỉ được thêm vào từ phiên bản số 2. Từ đó liệu ta có thể phán đoán là trong quá trình phát triển tính năng tối ưu code của trình biên dịch, nó lại yêu cầu ngược lại đặc tả phải thêm từ khóa volatile?

Như vậy “điều lệ” ban ra đã rõ ràng, tuy nhiên “chi bộ” Kernel Linux lại không tuân thủ, à cũng nói thêm là “chi bộ” này được “đồng chí” Tovard Linux thành lập từ 1990 (chú ý là “đồng chí” Ngô Thời Nhiệm không nằm trong chi bộ này). “chi bộ” này đã ra 1 “thông tư” nói không với volatile cơ bản có 2 điểm.
1. Trong mọi trường hợp trong kernel linux đều không thấy cần volatile.
2. Nếu cần thì đấy là BUG.
“Thông tư” đã viết rất rõ ràng nên ai muốn biết mời tham khảo. Tìm hiểu thêm về “chi bộ” này ta còn bắt gặp nhiều điểm bất thường khác nữa, phải chăng chính điều đó tạo nên đặc trưng cho Kernel Linux.

Tham khảo
http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html
http://publications.gbdirect.co.uk/c_book/

Creative Commons License