Tôi vấp phải một vấn đề buồn cười mà tôi không thể hiểu được.Lỗi tối ưu hóa trình biên dịch LLVM hoặc cái gì?
Bối cảnh là:
- LLVM 4.2 Trình biên dịch trên XCode
- biên soạn với C++ 11 hỗ trợ
- biên soạn với
-Os
- biên soạn cho ARMv7/armv7s kiến trúc
Bây giờ Tôi nhận ra có một vấn đề với một số mã có mặt ngay khi biên dịch với tối ưu hóa được kích hoạt.
Mã này là, nguyên văn:
static int foo(int tx, int sx, int w)
{
int vs = 60;
if (sx < vs*2 && tx > w - vs*2)
return (sx + w - tx);
else if (sx > w - vs*2 && tx < vs*2)
return -(w - sx + tx);
else
return sx - tx;
}
Bây giờ, bằng cách đi với LLĐB tôi bước mã để theo dõi xuống một lỗi lạ, và điều này dẫn tôi nhận ra rằng các chi nhánh đầu tiên của nếu được thực hiện với sự đóng góp
sx = 648
tx = 649
w = 768
vs = 60
(những giá trị này được lấy từ trực tiếp từ bảng người dân địa phương trong XCode, tôi không thể truy vấn LLĐB về vs
vì tôi đoán nó được tối ưu hóa.)
Nhánh đầu tiên sau đó là if (648 < 120 && ...
nên không có cách nào để lấy nó nhưng nó thực sự xảy ra. Nếu tôi biên dịch với -O0 thì lỗi sẽ biến mất.
Điều thú vị khác là thực tế là đối với sx = 647
và tx = 648
lỗi không xảy ra.
Bây giờ, mọi thứ là hai: hoặc tôi thiếu điều gì đó rõ ràng đến mức 10 giờ gỡ lỗi cấm tôi xem hoặc có một loại lỗi trong tối ưu hóa.
Bất kỳ manh mối nào?
Một số nền hơn, đây là ASM tạo:
.private_extern __ZN5Utils12wrapDistanceEiii
.globl __ZN5Utils12wrapDistanceEiii
.align 2
.code 16 @ @_ZN5Utils12wrapDistanceEiii
.thumb_func __ZN5Utils12wrapDistanceEiii
__ZN5Utils12wrapDistanceEiii:
.cfi_startproc
Lfunc_begin9:
@ BB#0:
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: wrapDistance:w <- R2+0
@DEBUG_VALUE: vs <- 60+0
sub.w r3, r2, #120
cmp r1, #119
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: wrapDistance:w <- R2+0
it le
cmple r3, r0
Ltmp42:
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: wrapDistance:w <- R2+0
ittt lt
sublt r0, r1, r0
Ltmp43:
addlt r0, r2
@DEBUG_VALUE: vs <- 60+0
bxlt lr
Ltmp44:
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: wrapDistance:w <- R2+0
@DEBUG_VALUE: vs <- 60+0
cmp r3, r1
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: wrapDistance:w <- R2+0
it lt
cmplt r0, #119
Ltmp45:
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: wrapDistance:w <- R2+0
itttt le
suble r1, r2, r1
Ltmp46:
addle r0, r1
Ltmp47:
rsble r0, r0, #0
@DEBUG_VALUE: vs <- 60+0
bxle lr
Ltmp48:
@DEBUG_VALUE: wrapDistance:tx <- R0+0
@DEBUG_VALUE: wrapDistance:sx <- R1+0
@DEBUG_VALUE: vs <- 60+0
subs r0, r1, r0
Ltmp49:
@DEBUG_VALUE: vs <- 60+0
bx lr
Ltmp50:
Lfunc_end9:
.cfi_endproc
Nếu tôi đặt một bản in, ví dụ như printf("%d < %d - %d",sx,vs*2,sx < vs*2)
trước mệnh đề if thì lỗi biến mất.
này test đơn giản exibits vấn đề:
for (int i = 0; i < 767; ++i)
{
printf("test: %d, %d, %d",i,i+1,Utils::wrapDistance(i+1, i, 768))
}
...
test: 641, 642, -1
test: 642, 643, -1
test: 643, 644, -1
test: 644, 645, -1
test: 645, 646, -1
test: 646, 647, -1
test: 647, 648, -1
test: 648, 649, -769
test: 649, 650, -1
test: 650, 651, -1
test: 651, 652, -1
test: 652, 653, -1
test: 653, 654, -1
test: 654, 655, -1
...
EDIT2
tôi quản lý để tái tạo các lỗi trong một mình chương trình độc, tôi vừa tạo ra một dự án iOS trống rỗng, sau đó tôi đã xác định chức năng hai lần, một lần trong AppDelegate.mm được gọi trực tiếp từ cùng một tệp và một tệp khác trong một tệp riêng biệt:
Test.h
#ifndef TEST_H_
#define TEST_H_
class Utils
{
public:
static int wrapDistance(int tx, int sx, int w);
};
#endif
Kiểm tra.cpp
#include "Test.h"
int Utils::wrapDistance(int tx, int sx, int w)
{
int vs = 60;
if (sx < vs*2 && tx > w - vs*2)
return (sx + w - tx);
else if (sx > w - vs*2 && tx < vs*2)
return -(w - sx + tx);
else
return sx - tx;
}
AppDelegate.mm
#import "AppDelegate.h"
#include "Test.h"
int wrapDistance(int tx, int sx, int w)
{
int vs = 60;
if (sx < vs*2 && tx > w - vs*2)
return (sx + w - tx);
else if (sx > w - vs*2 && tx < vs*2)
return -(w - sx + tx);
else
return sx - tx;
}
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
for (int i = 0; i < 767; ++i)
{
NSLog(@"test inside: %d, %d, %d",i,i+1,wrapDistance(i+1, i, 768));
NSLog(@"test outside: %d, %d, %d",i,i+1,Utils::wrapDistance(i+1, i, 768));
}
return YES;
}
...
OUTPUT
test inside: 644, 645, -1
test outside: 644, 645, -1
test inside: 645, 646, -1
test outside: 645, 646, -1
test inside: 646, 647, -1
test outside: 646, 647, -1
test inside: 647, 648, -1
test outside: 647, 648, -1
test inside: 648, 649, -1
test outside: 648, 649, -769
test inside: 649, 650, -1
test outside: 649, 650, -1
test inside: 650, 651, -1
test outside: 650, 651, -1
test inside: 651, 652, -1
test outside: 651, 652, -1
Như bạn có thể thấy, hành vi cho các chức năng được định nghĩa bên trong các tập tin từ đó là được gọi là chính xác, nhưng điều tương tự không áp dụng cho cái còn lại, cho thấy thứ cùng một lỗi. Nếu tôi buộc phải không inline chức năng bên trong với __attribute__ ((noinline))
thì cả hai hàm đều thất bại. Tôi thực sự mò mẫm vào bóng tối.
Nó rất có thể là bạn đang nhìn thấy hành vi undefined gây ra bởi một vấn đề ở nơi khác trong mã của bạn. Bạn có thể xây dựng một trường hợp thử nghiệm hoàn chỉnh không? –
Đây là trường hợp thử nghiệm hoàn chỉnh, chức năng không dựa vào bất kỳ đầu vào bên ngoài nào, đó là một chức năng tiện ích tĩnh được sử dụng chỉ để tính khoảng cách giữa hai ô trong môi trường được bao bọc. Lỗi luôn xảy ra với các giá trị đầu vào này. Tôi nên cố gắng cách ly nó khỏi dự án hoặc kiểm tra mã ASM tôi đoán. – Jack
Bởi "test-case", tôi có nghĩa là một [SSCCE] (http://sscce.org), có chứa mã trình điều khiển (tức là kiểm tra đơn vị hoặc bất cứ điều gì cần thiết để thể hiện hành vi). Như tôi chắc chắn bạn đang nhận thức, nhiều lỗi có thói quen sửa chữa bản thân một khi mã phiền hà được phân lập từ phần còn lại của chương trình;) –