2016-08-31 29 views
21

Hôm qua tôi thấy hành vi kỳ lạ này trong mã của tôi # C:Roslyn biên dịch tối ưu hóa đi nhân chức năng cuộc gọi với zero

Stack<long> s = new Stack<long>(); 

s.Push(1);   // stack contains [1] 
s.Push(2);   // stack contains [1|2] 
s.Push(3);   // stack contains [1|2|3] 

s.Push(s.Pop() * 0); // stack should contain [1|2|0] 

Console.WriteLine(string.Join("|", s.Reverse())); 

tôi cho rằng chương trình sẽ in 1|2|0 nhưng trên thực tế nó in 1|2|3|0.

Nhìn vào mã IL được tạo ra (thông qua ILSpy), bạn có thể thấy rằng s.Pop() * 0 được tối ưu hóa để đơn giản 0:

// ... 
IL_0022: ldloc.0 
IL_0023: ldc.i4.0 
IL_0024: conv.i8 
IL_0025: callvirt instance void class [System]System.Collections.Generic.Stack`1<int64>::Push(!0) 
// ... 

ILSpy decompilation:

Stack<long> s = new Stack<long>(); 
s.Push(1L); 
s.Push(2L); 
s.Push(3L); 
s.Push(0L); // <- the offending line 
Console.WriteLine(string.Join<long>("|", s.Reverse<long>())); 

Trước tiên tôi thử nghiệm này ban đầu dưới Windows 7 với Visual Studio 2015 Update 3 với cả chế độ Release (/optimize) và chế độ Debug và với các khung mục tiêu khác nhau (4.0, 4.5, 4.6 và 4.6.1). Trong tất cả 8 trường hợp, kết quả là giống nhau (1|2|3|0).

Sau đó, tôi đã thử nghiệm nó trong Windows 7 với Visual Studio 2013 Update 5 (một lần nữa với tất cả các kết hợp của chế độ Release/Debug và khung đích). Thật ngạc nhiên khi tuyên bố ở đây là không phải là được tối ưu hóa và mang lại kết quả mong đợi 1|2|0.

Vì vậy, tôi có thể kết luận rằng hành vi này không phụ thuộc vào /optimize cũng như cờ khung mục tiêu mà đúng hơn là trên phiên bản trình biên dịch đã sử dụng.

Không quan tâm Tôi đã viết một mã tương tự trong C++ và biên dịch nó bằng phiên bản gcc hiện tại. Ở đây một cuộc gọi hàm nhân với số không được tối ưu hóa đi và chức năng được thực hiện đúng.

Tôi nghĩ một tối ưu hóa như vậy sẽ chỉ hợp lệ nếu stack.Pop() là một hàm thuần túy (chắc chắn là không). Nhưng tôi do dự khi gọi đây là lỗi, tôi cho rằng đó chỉ là một tính năng mà tôi không biết?

Đây có phải là "tính năng" ở bất kỳ đâu được ghi lại và có cách nào (dễ) để vô hiệu hóa tối ưu hóa này không?

+4

Yuck, vâng, tôi có thể repro với VS2015 Cập nhật 2. Roslyn đã được một máy phát điện lỗi lớn. Nhấp vào nút [Vấn đề mới] (https://github.com/dotnet/roslyn/issues) để báo cáo. Limp cùng với việc sử dụng một biến để lưu trữ giá trị trả về Pop, nó sẽ bị loại bỏ bởi trình tối ưu hóa jitter. –

+0

Điều này rất có thể nó không thể được trả lời mà không có một liên kết đến một nguồn tài nguyên trang web tắt (mà làm cho chủ đề này tắt). Đây là một báo cáo lỗi tuyệt vời mà nên được gửi ở đây https://github.com/dotnet/roslyn/issues Tất nhiên, một người nào đó trong nhóm có thể trả lời ... có lẽ Jared Parsons? – Will

+0

@HansPassant: Tôi cho rằng đây không phải là lỗi mà là một tính năng :). Nhưng nếu đó là tôi sẽ mở một vấn đề và thay đổi mã của tôi-máy phát điện cho bây giờ rõ ràng gọi Stack.Pop() trong dòng riêng của nó. – Mikescher

Trả lời

12

Vâng, đó chắc chắn là lỗi. < expr> * 0, không được tối ưu hóa thành 0 nếu < expr> có tác dụng phụ.

Cảm ơn bạn đã báo cáo sự cố !!

Bạn có thể theo dõi sự tiến bộ của lỗi/sửa chữa tại https://github.com/dotnet/roslyn/issues/13486

+1

Cảm ơn các bạn vì đã quá nhạy cảm với các vấn đề như thế này. – Will

+1

Bản sửa lỗi đã được hợp nhất trong https://github.com/dotnet/roslyn/pull/13501 – VSadov

+0

Thật tuyệt vời. Tôi thực sự hạnh phúc về sự phát triển của MS trong thời gian gần đây. Các bạn có thể đi qua nhóm điện thoại và đá chúng vào các hạt không? Tôi lo sợ cho nền tảng WP. Nghiêm túc, tôi sợ nó. – Will

Các vấn đề liên quan