내가 이해할 수없는 재미있는 문제를 발견했습니다.LLVM 컴파일러 최적화 버그 또는 무엇?
배경은 다음과 같습니다
지금하는 ARMv7/armv7s 아키텍처 용으로 컴파일 -Os
- LLVM 4.2 컴파일러 최적화가 활성화 된 상태에서 컴파일 할 때 나타나는 코드에 문제가 있음을 깨달았습니다. 나는 이상한 버그를 추적하는 코드를 강화 LLDB로 이동하여 지금
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; }
, 이것은 첫 번째 분기가 입력을 가지고가는 경우에 실현하기 위해 나를 인도 :
코드는 그대로입니다
(이 값은 엑스 코드에서 지역 주민 테이블에서 직접에서 가져가, 나는 그것을 최적화됩니다 생각 때문에 약sx = 648 tx = 649 w = 768 vs = 60
vs
lldb 쿼리 할 수 아니에요.)첫 번째 지점은
if (648 < 120 && ...
이므로 취할 방법이 없어야하지만 실제로 발생합니다. -O0으로 컴파일하면 버그가 사라집니다.더 재미있는 점은
sx = 647
과tx = 648
의 경우 버그가 발생하지 않는다는 것입니다.이제 두 가지가 있습니다. 또는 10 시간의 디버깅으로 인해 나를 보지 못하게하거나 최적화에 버그가 있습니다.
실마리가 있습니까?
좀 더 배경, 이것은 ASM이 생성됩니다
.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
그때 버그가 사라지는 경우 절하기 전에 인쇄, 예를 들어
printf("%d < %d - %d",sx,vs*2,sx < vs*2)
을 배치합니다.이 간단한 테스트 케이스
문제 exibits :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
내가 독립 실행 형 프로그램의 버그를 재현 관리를, 난 그냥 빈 아이폰 OS 프로젝트를 만든 다음, 나는 정의 AppDelegate.mm에서 한 번은 동일한 파일에서 직접 호출하고 다른 파일은 별도의 파일에서 두 번 호출 할 수 있습니다.
Test.h
#ifndef TEST_H_ #define TEST_H_ class Utils { public: static int wrapDistance(int tx, int sx, int w); }; #endif
테스트.당신이 볼 수 있듯이 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; } ...
는 파일 내부에 정의 된 함수의 동작은있는
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
이다 OUTPUT 라는 것은 정확하지만 다른 것은 똑같은 것을 적용하지 않는다. 같은 버그. 강제로
__attribute__ ((noinline))
으로 내부 함수를 인라인하지 않으면 두 함수가 실패합니다. 나는 어둠 속으로 정말로 들떠있다.
코드의 다른 곳에 문제가 있습니다. 완전한 테스트 케이스를 만들 수 있습니까? –
이것은 완전한 테스트 케이스입니다.이 함수는 외부 입력에 의존하지 않고 래핑 된 환경에서 두 타일 간의 거리를 계산하는 데 사용되는 정적 유틸리티 함수입니다. 버그는 항상 이러한 입력 값과 함께 발생합니다. 내가 프로젝트에서 격리 시키거나 ASM 코드를 확인하려고 노력해야한다. – Jack
"test-case"는 드라이버 코드 (예 : 단위 테스트 또는 동작을 표시하는 데 필요한 것)가 포함 된 [SSCCE] (http://sscce.org)를 의미합니다. 여러분이 알고 계시 겠지만, 많은 버그는 문제가있는 코드가 프로그램의 나머지 부분과 분리되면 스스로 수정하는 습관이 있습니다;) –