【发布时间】:2013-07-05 07:46:45
【问题描述】:
我偶然发现了一个我无法理解的有趣问题。
背景是:
- 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 表,我无法查询 lldb 关于 vs 的信息,因为我猜它得到了优化。)
然后第一个分支是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 子句之前放置一个打印,例如 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 中直接从同一个文件中调用,另一次在单独的文件中调用文件:
Test.h
#ifndef TEST_H_
#define TEST_H_
class Utils
{
public:
static int wrapDistance(int tx, int sx, int w);
};
#endif
Test.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)) 内联内部函数,那么这两个函数都会失败。我真的在黑暗中摸索。
【问题讨论】:
-
您很可能会看到由代码中其他地方的问题引起的未定义行为。你能构建一个完整的测试用例吗?
-
这是完整的测试用例,该函数不依赖任何外部输入,它是一个静态实用函数,仅用于计算包装环境中两个图块之间的距离。这些输入值总是会出现错误。我应该尝试将它与项目隔离或检查我猜的 ASM 代码。
-
“测试用例”是指SSCCE,它将包含驱动程序代码(即单元测试或展示行为所需的任何内容)。我相信您知道,一旦麻烦的代码与程序的其余部分隔离开来,许多错误都有自己修复的习惯;)
-
当然,有可能(但不太可能)确实存在编译器错误。但我们需要证据(例如相应的 ASM 输出)...
-
对@OliCharlesworth 的评论进行猜测,如果优化器将 120 放在一个常量表中,该表被其他代码中的错误指针位置写入所破坏,它可以解释您所看到的一切。
标签: c++ xcode llvm compiler-optimization