Trong khi biên soạn một dự án lớn hơn với tiếng lóng, tôi tình cờ gặp một lỗi khó chịu.Lỗi tối ưu hóa LLVM hoặc hành vi không xác định?
xem xét ví dụ nhỏ sau:
unsigned long int * * fee();
void foo(unsigned long int q)
{
unsigned long int i,j,k,e;
unsigned long int pows[7];
unsigned long int * * table;
e = 0;
for (i = 1; i <= 256; i *= q)
pows[e++] = i;
pows[e--] = i;
table = fee(); // need to set table to something unknown
// here, otherwise the compiler optimises
// parts of the loops below away
// (and no bug occurs)
for (i = 0; i < q; i++)
for (j = 0; j < e; j++)
((unsigned char*)(*table) + 5)[i*e + j] = 0; // bug here
}
Để theo sự hiểu biết của tôi mã này không vi phạm các tiêu chuẩn C trong bất kỳ cách nào, mặc dù dòng cuối cùng dường như lúng túng (trong dự án thực tế, mã như thế này xuất hiện do sử dụng quá nhiều macro tiền xử lý).
Biên dịch điều này bằng tiếng kêu (phiên bản 3.1 hoặc cao hơn) ở cấp tối ưu hóa -O1 hoặc cao hơn dẫn đến việc viết mã sai vị trí trong bộ nhớ.
Các bộ phận quan trọng của tập tin lắp ráp sản xuất bởi kêu vang/LLVM đọc như sau: (Đây là cú pháp GAS, như vậy để những người bạn của những người đang sử dụng để Intel: Hãy coi chừng!)
[...]
callq _fee
leaq 6(%rbx), %r8 ## at this point, %rbx == e-1
xorl %edx, %edx
LBB0_4:
[...]
movq %r8, %rsi
imulq %rdx, %rsi
incq %rdx
LBB0_6:
movq (%rax), %rcx ## %rax == fee()
movb $0, (%rcx,%rsi)
incq %rsi
[conditional jumps back to LBB0_6 resp. LBB0_4]
[...]
Trong khác từ, hướng dẫn làm
(*table)[i*(e+5) + j] = 0;
thay vì dòng cuối cùng được viết ở trên. Sự lựa chọn của + 5
là tùy ý, thêm (hoặc trừ) các số nguyên khác dẫn đến cùng một hành vi. Vì vậy - đây có phải là lỗi trong tối ưu hóa của LLVM hay không có hành vi không xác định xảy ra ở đây?
Chỉnh sửa: Cũng lưu ý rằng lỗi biến mất nếu tôi bỏ dàn diễn viên (unsigned char*)
ở dòng cuối cùng. Nói chung, lỗi có vẻ khá nhạy cảm với bất kỳ thay đổi nào.
Không thể thấy phép nhân bằng 5 trong mã lắp ráp ở trên (nhưng sau đó tôi được sử dụng để lắp ráp ARM hơn Intel, nếu là Intel :-)), nhưng dòng cuối cùng của mã C dịch thành '* ((unsigned char *) (* table) + 5 + i * e + j) ', vì vậy ... bạn có chắc chắn rằng bạn đặt những niềng răng xung quanh" e + 5 "trong giải thích của bạn về đầu ra lắp ráp một cách chính xác? – user2116939
Vâng, tôi khá chắc chắn. Đây là cú pháp GAS, không phải Intel, do đó, 'movq% r8,% rsi' và' imulq% rdx,% rsi' có nghĩa là '% rsi' sẽ giữ' (% rbx + 6) *% rdx = (e + 5) *% rdx'. –
Có, bây giờ tôi có thể thấy điều này. Nó trông giống như một lỗi trình tối ưu hóa vì mã là đủ kosher ngay cả khi một chút lạ (nhưng sau đó các macro có thể tạo ra đầu ra lạ). – user2116939