LLVM编译器优化错误或什么
LLVM compiler optimization bug or what?
我无意中发现了一个我无法理解的有趣问题。
背景是:
- XCode上的LLVM 4.2编译器
- 编译时支持c++11
- 用
-Os
编译 - 为armv7/armv7s体系结构编译
现在我意识到,在启用优化的情况下进行编译时,某些代码出现了问题。
代码是:
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;
}
现在,通过使用LLDB,我逐步执行代码来追踪一个奇怪的错误,这让我意识到if的第一个分支是用输入实现的
sx = 648
tx = 649
w = 768
vs = 60
(这些值直接取自XCode中的locals表,我无法查询关于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
如果我在If子句之前放置一个print,例如printf("%d < %d - %d",sx,vs*2,sx < vs*2)
,那么错误就会消失。
这个简单的测试用例解决了问题:
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
我设法在一个独立的程序中重现了这个错误,我只是创建了一个空的iOS项目,然后我定义了两次函数,一次是在AppDelegate.mm中直接从同一个文件调用,另一次在一个单独的文件中:
测试.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
正如您所看到的,在被调用的文件中定义的函数的行为是正确的,但同样的事情不适用于另一个,这显示了相同的错误。如果我强制不使用__attribute__ ((noinline))
内联内部函数,那么这两个函数都会失败。我真的在黑暗中摸索。
首先,您的测试用例暗示它实际上错误地获取了else if
分支。
但我收回了它;在生成的ASM中似乎确实存在一个错误*
以下是针对失败测试的ASM的格式化/注释版本:
% 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
条件(3)和(4)应该一起工作以实现两个子表达式的逻辑and。理论上,只有当块(3)被执行并且后续比较设置了适当的状态标志时,块(4)才会被执行**
然而,这是错误的实施。比较(3)设置了Z
,这意味着条件(3)没有被触发(它需要N!=V
),所以条件(4)没有被执行。到目前为止还好。但它足以触发条件(4)(它需要(Z==1) || (N!=V)
),从而导致您看到的问题。
总之,这里有四种可能性:
- 针对ARM7的LLVM后端确实存在一个错误
- 您提供的C代码实际上并不是您正在编译的代码
- 您在其他地方有一些无效的C代码,这些代码会触发未定义的行为,导致生成无意义的ASM作为副作用
- 我上面的分析是错误的
*虽然现在是凌晨12:40,所以我可能搞错了
**http://blogs.arm.com/software-enablement/206-condition-codes-1-condition-flags-and-codes/
- 警告处理为错误这里有什么问题
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- 0-1背包代码中的错误.我的代码中有什么错误
- 是什么导致了Unity 3D中的"错误线程异常"?
- 如果我这样写,我的单例类会导致什么错误
- 为什么输出不同以及此代码中有什么错误
- 我的代码中有什么错误.我尝试在类中使用数组,但出现逻辑错误
- 使用 fopen 而不是 Visual Studio 建议的 fopen_s 有什么错误
- 我的 C++11 测试表明 sort(vector<string>) 甚至比 C++03 慢,有什么错误吗?
- 这个 c++ 结构有什么错误
- 什么错误的逻辑导致我的链表的这个 push_back(..) 函数失败?
- 我的类定义中有什么错误?
- realloc C 函数的可能包装函数有什么错误?
- 什么错误无法理解
- C++:使用STL矢量时有什么错误
- 下面的代码中有什么错误,应该如何更正
- 我错过了什么?c++错误
- 这里面有什么错误?字符串赋值
- 如果将此函数放在泛型库中,会导致什么错误?
- 我做错了什么? c++错误