Là trình biên dịch được phép để liên tục gấp một biến động cục bộ?


25

Hãy xem xét mã đơn giản này:

void g();

void foo()
{
    volatile bool x = false;
    if (x)
        g();
}

https://godbolt.org/z/I2kBY7

Bạn có thể thấy rằng không phải gcccũng không clangtối ưu hóa cuộc gọi tiềm năng g. Theo cách hiểu của tôi là đúng: Máy trừu tượng giả định rằng volatilecác biến có thể thay đổi bất cứ lúc nào (do được ánh xạ phần cứng), do đó, việc liên tục gấp việc falsekhởi tạo vào ifkiểm tra sẽ là sai.

Nhưng MSVC loại bỏ ghoàn toàn cuộc gọi (giữ cho việc đọc và ghi vào volatilemặc dù!). Đây có phải là hành vi tuân thủ tiêu chuẩn?


Bối cảnh: Thỉnh thoảng tôi sử dụng loại cấu trúc này để có thể bật / tắt đầu ra gỡ lỗi khi đang di chuyển: Trình biên dịch phải luôn đọc giá trị từ bộ nhớ, do đó việc thay đổi biến / bộ nhớ trong quá trình gỡ lỗi sẽ điều chỉnh luồng điều khiển cho phù hợp . Đầu ra MSVC không đọc lại giá trị nhưng bỏ qua nó (có lẽ là do việc gấp liên tục và / hoặc loại bỏ mã chết), điều này tất nhiên đánh bại ý định của tôi ở đây.


Chỉnh sửa:

  • Việc loại bỏ các lần đọc và ghi volatileđược thảo luận ở đây: Có cho phép trình biên dịch tối ưu hóa một biến động cục bộ không? (cảm ơn Nathan!). Tôi nghĩ rằng tiêu chuẩn rất rõ ràng rằng những gì đọc và viết phải xảy ra. Nhưng cuộc thảo luận đó không đề cập đến việc liệu trình biên dịch có lấy kết quả của những lần đọc được cấp và tối ưu hóa dựa trên điều đó hay không. Tôi cho rằng điều này chưa được / không xác định trong tiêu chuẩn, nhưng tôi rất vui nếu ai đó chứng minh tôi sai.

  • Tất nhiên tôi có thể tạo xmột biến không cục bộ để giải quyết vấn đề. Câu hỏi này là nhiều hơn tò mò.


3
Điều này trông giống như một lỗi biên dịch rõ ràng với tôi.
Sam Varshavchik

1
Theo tôi biết điều này là hợp pháp theo quy tắc như thể. Trình biên dịch có thể chứng minh rằng mặc dù đối tượng không ổn định, không có cách nào để trạng thái của nó được sửa đổi để có thể gập lại. Tôi không đủ tự tin để đưa ra câu trả lời, nhưng tôi cảm thấy nó đúng.
NathanOliver

3
Nhưng tôi nghĩ rằng lập luận của OP rằng biến có thể được sửa đổi bởi trình gỡ lỗi là hợp lý. Có lẽ ai đó nên nộp báo cáo lỗi với MSVC.
Brian

2
@cantlyguy Ngay cả khi bạn loại bỏ kết quả và / hoặc giả sử giá trị chính xác, bạn vẫn đọc nó.
Ded repeatator

2
Điều thú vị là nó chỉ làm điều đó cho x64. Phiên bản x86 vẫn gọi g () godbolt.org/z/nc3Y-f
Jerry Jeremiah

Câu trả lời:


2

Tôi nghĩ rằng [intro.execut] (số đoạn khác nhau) có thể được sử dụng để giải thích hành vi MSVC:

Một phiên bản của mỗi đối tượng có thời lượng lưu trữ tự động được liên kết với mỗi mục nhập vào khối của nó. Một đối tượng như vậy tồn tại và giữ lại giá trị được lưu trữ cuối cùng của nó trong quá trình thực thi khối và trong khi khối bị treo ...

Tiêu chuẩn không cho phép loại bỏ việc đọc thông qua giá trị biến động, nhưng đoạn trên có thể được hiểu là cho phép dự đoán giá trị false.


BTW, Tiêu chuẩn C (N1570 6.2.4 / 2) nói rằng

Một đối tượng tồn tại, có một địa chỉ không đổi và giữ lại giá trị được lưu trữ cuối cùng trong suốt vòng đời của nó. 34


34) Trong trường hợp đối tượng dễ bay hơi, cửa hàng cuối cùng không cần phải rõ ràng trong chương trình.

Không rõ liệu có thể có một cửa hàng không rõ ràng vào một đối tượng với thời gian lưu trữ tự động trong mô hình bộ nhớ / đối tượng C.


Đồng ý rằng trình biên dịch có thể biết khi nào các cửa hàng không rõ ràng có thể có trên nền tảng đích
MM

1
Vì vậy, nếu điều này là đúng, thì các đối tượng dễ bay hơi cục bộ (ít nhất là trên MSVC) hoàn toàn vô nghĩa? Có bất cứ điều gì thêm vào volatilemua bạn (ngoài việc đọc / ghi không cần thiết) nếu nó bị bỏ qua cho mục đích tối ưu hóa?
Max Langhof

1
@MaxLanghof Có một sự khác biệt giữa hoàn toàn vô nghĩa và không có hiệu quả như bạn muốn / mong đợi.
Ded repeatator

1
@MaxLanghof Kết quả trung gian của các phép tính dấu phẩy động đôi khi được thăng cấp lên thanh ghi 80 bit gây ra các vấn đề chính xác. Tôi tin rằng đây là một gcc-ism và được tránh bằng cách tuyên bố tất cả các nhân đôi như vậy là dễ bay hơi. Xem: gcc.gnu.org/ormszilla/show_orms.cgi?id=323
ForeverLearning

1
@philipxy PS Xem câu trả lời của tôi Tôi biết rằng quyền truy cập được xác định theo triển khai. Câu hỏi không phải là về quyền truy cập (đối tượng được truy cập), mà là dự đoán về giá trị.
Luật sư ngôn ngữ

2

TL; DR Trình biên dịch có thể làm bất cứ điều gì nó muốn trên mỗi lần truy cập dễ bay hơi. Nhưng tài liệu này phải cho bạn biết .-- "Các ngữ nghĩa của một truy cập thông qua một giá trị không ổn định được xác định theo triển khai."


Tiêu chuẩn xác định cho một chương trình cho phép các chuỗi "truy cập dễ bay hơi" và "hành vi có thể quan sát" khác (đạt được thông qua "tác dụng phụ") mà việc triển khai phải tuân thủ theo "quy tắc" như thể ".

Nhưng tiêu chuẩn nói (nhấn mạnh đậm của tôi):

Bản nháp làm việc, Tiêu chuẩn cho ngôn ngữ lập trình C ++
Số tài liệu: N4659
Ngày: 2017-03-21

§ 10.1.7.1 Các vòng loại cv

5 Các ngữ nghĩa của một truy cập thông qua một giá trị dễ bay hơi được xác định theo thực hiện. [Càng]

Tương tự cho các thiết bị tương tác (nhấn mạnh đậm của tôi):

§ 4.6 Thực hiện chương trình

5 Việc thực hiện tuân thủ thực hiện một chương trình được định dạng tốt sẽ tạo ra hành vi có thể quan sát tương tự như một trong những thực thi có thể có của thể hiện tương ứng của máy trừu tượng có cùng chương trình và cùng một đầu vào. [...]

7 Các yêu cầu tối thiểu về việc thực hiện tuân thủ là:

(7.1) - Truy cập thông qua các giá trị dễ bay hơi được đánh giá đúng theo các quy tắc của máy trừu tượng.
(7.2) - Khi chấm dứt chương trình, tất cả dữ liệu được ghi vào tệp phải giống hệt với một trong những kết quả có thể thực hiện chương trình theo ngữ nghĩa trừu tượng sẽ tạo ra.
(7.3) - Động lực đầu vào và đầu ra của các thiết bị tương tác sẽ diễn ra theo kiểu sao cho đầu ra thực sự được phân phối trước khi chương trình chờ đầu vào. Những gì tạo thành một thiết bị tương tác được xác định thực hiện.

Chúng được gọi chung là hành vi có thể quan sát được của chương trình. [...]

(Dù sao, mã cụ thể được tạo cho một chương trình không được chỉ định bởi tiêu chuẩn.)

Vì vậy, mặc dù tiêu chuẩn nói rằng các truy cập dễ bay hơi không thể bị loại bỏ khỏi các chuỗi trừu tượng của các tác dụng phụ của máy trừu tượng và các hành vi có thể quan sát được mà một số mã (có thể) xác định, bạn không thể mong đợi bất cứ điều gì được phản ánh trong mã đối tượng hoặc thế giới thực hành vi trừ khi tài liệu trình biên dịch của bạn cho bạn biết những gì cấu thành một truy cập dễ bay hơi . Ditto cho các thiết bị tương tác.

Nếu bạn quan tâm đến tính dễ bay hơi, hãy xem các chuỗi trừu tượng của các tác dụng phụ của máy trừu tượng và / hoặc các hành vi có thể quan sát được do một số mã (có thể) xác định sau đó nói như vậy . Nhưng nếu bạn quan tâm đến mã đối tượng tương ứng được tạo ra thì bạn phải giải thích điều đó trong ngữ cảnh của trình biên dịch & biên dịch của bạn .

Những người thường xuyên tin tưởng sai lầm rằng đối với các truy cập không ổn định, việc đánh giá / đọc máy trừu tượng gây ra việc đọc được thực hiện & việc gán / ghi máy trừu tượng gây ra việc ghi thực hiện. Không có cơ sở cho niềm tin này vắng mặt tài liệu thực hiện nói như vậy. Khi / iff triển khai nói rằng nó thực sự làm một cái gì đó dựa trên "quyền truy cập dễ bay hơi", mọi người sẽ có lý khi hy vọng rằng một cái gì đó - có thể, tạo ra một mã đối tượng nhất định.


1
Ý tôi là, điều này có nghĩa là "nếu tôi nghĩ ra một cỗ máy mà tất cả các tác dụng phụ được đề cập là không có tác dụng thì tôi có triển khai C ++ hợp pháp bằng cách biên dịch mọi chương trình thành không hoạt động" . Tất nhiên chúng tôi quan tâm đến các hiệu ứng thực tế có thể quan sát được, vì các hiệu ứng phụ của máy trừu tượng là trừu tượng về mặt tautologological. Điều này đòi hỏi một số khái niệm cơ bản về tác dụng phụ và tôi sẽ cho rằng "truy cập dễ bay hơi dẫn đến hướng dẫn truy cập bộ nhớ rõ ràng" là một phần của điều đó (ngay cả khi tiêu chuẩn không quan tâm), vì vậy tôi không thực sự mua "nói nếu bạn muốn mã thay vì ngữ nghĩa trừu tượng ". Tuy nhiên, +1.
Max Langhof

Có, chất lượng thực hiện có liên quan. Tuy nhiên, ngoài việc ghi vào tệp, "có thể quan sát" [sic] được xác định theo triển khai. Nếu bạn muốn có thể thiết lập các điểm dừng, truy cập bộ nhớ thực tế nhất định, đã bỏ qua 'dễ bay hơi', v.v. trên các lần đọc và ghi dễ bay hơi trừu tượng thì bạn phải đưa trình biên dịch trình biên dịch của mình ra mã phù hợp . PS Trong C ++, "hiệu ứng phụ" không được sử dụng cho hành vi quan sát trên mỗi se, nó được sử dụng để mô tả đánh giá thứ tự từng phần của biểu hiện phụ.
philipxy

Muốn giải thích [sic]? Bạn đang trích dẫn nguồn nào và lỗi gì?
Max Langhof

Re sic Tôi chỉ có nghĩa là một cỗ máy trừu tượng có thể quan sát được chỉ có thể quan sát được trong thế giới thực nếu & cách thức triển khai nói.
philipxy

1
Bạn đang nói rằng một triển khai có thể tuyên bố rằng không có thứ gọi là thiết bị tương tác, vì vậy bất kỳ chương trình nào cũng có thể làm bất cứ điều gì, và nó vẫn sẽ đúng? (Lưu ý: Tôi không hiểu sự nhấn mạnh của bạn trên các thiết bị tương tác.)
tò mò

-1

Tôi tin rằng nó là hợp pháp để bỏ qua kiểm tra.

Đoạn văn mà mọi người thích trích dẫn

34) Trong trường hợp đối tượng dễ bay hơi, cửa hàng cuối cùng không cần phải rõ ràng trong chương trình

không ngụ ý rằng việc triển khai phải cho rằng các cửa hàng như vậy có thể xảy ra bất cứ lúc nào hoặc cho bất kỳ biến động nào. Một triển khai biết cửa hàng nào là có thể. Ví dụ, hoàn toàn hợp lý khi giả định rằng việc ghi ngầm định đó chỉ xảy ra đối với các biến dễ bay hơi được ánh xạ tới các thanh ghi thiết bị và việc ánh xạ như vậy chỉ có thể đối với các biến có liên kết ngoài. Hoặc một triển khai có thể giả định rằng việc ghi như vậy chỉ ghi vào các vị trí bộ nhớ được căn chỉnh từ, có kích thước từ.

Phải nói rằng, tôi nghĩ rằng hành vi MSVC là một lỗi. Không có lý do trong thế giới thực để tối ưu hóa cuộc gọi. Tối ưu hóa như vậy có thể được tuân thủ, nhưng nó là xấu xa không cần thiết.


Bạn có thể giải thích tại sao nó xấu? Trong mã cho thấy, hàm theo nghĩa đen có thể không bao giờ được gọi.
David Schwartz

@DavidSchwartz Bạn chỉ có thể kết luận rằng sau khi bạn chỉ định ngữ nghĩa của các biến dễ bay hơi cục bộ (xem đoạn trích dẫn ở trên). Bản thân tiêu chuẩn lưu ý rằng volatileđược coi là một gợi ý cho việc thực hiện rằng giá trị có thể thay đổi bằng cách không biết đến việc thực hiện.
Max Langhof

@MaxLanghof Không có cách nào để triển khai có thể xử lý chính xác một cái gì đó chưa biết đối với nó. Những nền tảng hữu ích thực sự làm là chỉ định những gì bạn có thể và không thể sử dụng volatilecho nền tảng đó và ngoài đặc điểm kỹ thuật đó, nó sẽ luôn luôn là một trò nhảm nhí.
David Schwartz

@DavidSchwartz Tất nhiên là có thể - bằng cách tuân theo ngữ nghĩa (đặc biệt là đọc và viết) của máy trừu tượng. Nó có thể không thể tối ưu hóa chính xác - đó là điểm của tiêu chuẩn. Bây giờ, đó là một ghi chú và do đó không quy định, và như cả hai chúng tôi đã nói, việc thực hiện có thể chỉ định những gì volatilelàm và tuân theo điều đó. Quan điểm của tôi là bản thân mã (theo máy trừu tượng / tiêu chuẩn C ++) không cho phép bạn xác định liệu gcó thể được gọi hay không.
Max Langhof

@DavidSchwartz Mã không hiển thị bất cứ điều gì như vậy, bởi vì sự vắng mặt của việc ghi ngầm định không tuân theo mã.
n. 'đại từ' m.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.