2013-07-08 31 views
9

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 = 647tx = 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.

+0

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? –

+0

Đâ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

+0

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;) –

Trả lời

5

Thứ nhất, trường hợp kiểm tra của bạn ngụ ý rằng nó thực sự sai khi lấy chi nhánh else if.

Nhưng tôi lấy lại; có vẻ như là một lỗi trong ASM kết quả. *

Đây là một phiên bản được định dạng/chú thích của ASM của bạn cho thử nghiệm thất bại:

% r0 = tx = 649 
% r1 = sx = 648 
% r2 = w = 768 

% w - vs*2 
sub.w r3, r2, #120   % r3 = 648 

% if (sx < vs*2) 
cmp r1, #119 
it le      % (1) Not taken 
    % if ((w - vs*2) < tx) 
    cmple r3, r0 
ittt lt     % (2) Not taken 
    % return (sx + w - tx) 
    sublt r0, r1, r0 
    addlt r0, r2 
    bxlt lr 

% if ((w - vs*2) < sx) 
cmp r3, r1 
it lt      % (3) Not taken 
    % if (tx < vs*2) 
    cmplt r0, #119 
itttt le     % (4) Taken! <<<<<<<<<<<<< 
    % return -(w - sx + tx) 
    suble r1, r2, r1 
    addle r0, r1 
    rsble r0, r0, #0 
    bxle lr 

% return sx - tx 
subs r0, r1, r0 
bx lr 

Conditionals (3) và (4) có nghĩa vụ phải làm việc cùng nhau để đạt được một logic AND của hai biểu thức con. Về lý thuyết, khối (4) chỉ được thực hiện nếu khối (3) được thực hiện so sánh tiếp theo đặt cờ trạng thái thích hợp. **

Tuy nhiên, điều này được triển khai không chính xác. So sánh (3) bộ Z, có nghĩa là điều kiện (3) không được kích hoạt (yêu cầu N!=V), vì vậy điều kiện (4) không được thực thi. Tốt cho đến nay. Nhưng nó đủ để kích hoạt điều kiện (4) (yêu cầu (Z==1) || (N!=V)), dẫn đến sự cố bạn thấy.

Để tóm tắt, có bốn khả năng ở đây:

  1. Có thực sự là một lỗi trong nhắm mục tiêu ARM7 backend LLVM.
  2. Mã C bạn cung cấp không thực sự là mã bạn đang biên soạn.
  3. Bạn có một số mã C không hợp lệ ở nơi khác đang kích hoạt hành vi không xác định, dẫn đến ASM vô nghĩa được tạo dưới dạng tác dụng phụ.
  4. Phân tích của tôi ở trên không chính xác!


* Mặc dù đó là 12:40 ngay bây giờ, vì vậy tôi có thể bị nhầm lẫn ...

** http://blogs.arm.com/software-enablement/206-condition-codes-1-condition-flags-and-codes/

+0

Cảm ơn bạn đã phân tích, không bao giờ làm việc trực tiếp với cánh tay vì vậy tôi sẽ cần nhiều thời gian hơn để xem hướng dẫn và kiến ​​trúc chung được sử dụng trong phân nhánh. Bây giờ tôi luôn có xu hướng loại trừ lỗi trình biên dịch, vì 99,9999% thời gian là lỗi của nhà phát triển nhưng tôi loại trừ giả thuyết 2 và 4. Vì vậy, có thể một số mã không hợp lệ kích hoạt hành vi không xác định nhưng tôi không thấy cách này có thể tạo sai nhị phân? Nếu không, đó thực sự là một lỗi. – Jack

+0

@Jack: Trình biên dịch được cho phép giả định rằng mã của bạn là hợp lệ và để tối ưu hóa cho phù hợp. Nếu mã của bạn là bằng cách nào đó không hợp lệ, những tối ưu hóa này có thể không còn có ý nghĩa (do đó hành vi không xác định). Tôi muốn nói rằng nó không chắc rằng một vấn đề như vậy có thể biểu hiện trong một chức năng độc lập như thế này, mặc dù. (Tuy nhiên, tôi cũng nghĩ rằng rất khó có thể có lỗi trình biên dịch ...) –

+0

Tôi đã có thể tạo lại lỗi trong một dự án trống. Tất cả những gì tôi cần làm là đặt hàm vào một tệp riêng biệt và gọi nó từ 'applicationDidFinishLaunching:' của ủy nhiệm ứng dụng. Điều kỳ lạ: nếu tôi giữ chức năng trong cùng một tập tin của đại biểu thì lỗi đó không xảy ra. Nếu tôi di chuyển nó trên tập tin riêng của nó thì nó xảy ra. Kiểm tra chỉnh sửa của tôi. – Jack

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