gpt4 book ai didi

c++ - LLVM 编译器优化错误还是什么?

转载 作者:可可西里 更新时间:2023-11-01 16:39:59 26 4
gpt4 key购买 nike

我偶然发现了一个我无法理解的有趣问题。

背景是:

  • 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 = 647tx = 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)在 if 子句之前,bug 就消失了。

这个简单的测试用例展示了问题:

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) 应该一起工作以实现两个子表达式的逻辑与。理论上, block (4) 仅在 block (3) 被执行时执行,并且随后的比较设置了适当的状态标志。**

但是,这是错误地实现的。比较(3)设置了Z,也就是说条件(3)没有触发(要求N!=V),所以条件(4)不执行。到目前为止还好。但它足以触发条件 (4)(它需要 (Z==1) || (N!=V)),导致您看到的问题。

总结一下,这里有四种可能:

  1. 针对 ARM7 的 LLVM 后端确实存在一个错误。
  2. 您提供的 C 代码实际上并不是您正在编译的代码。
  3. 您在其他地方有一些无效的 C 代码会触发未定义的行为,从而导致生成无意义的 ASM 作为副作用。
  4. 我上面的分析是错误的!


* 虽然现在是中午 12 点 40 分,所以我可能弄错了......

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

关于c++ - LLVM 编译器优化错误还是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17518823/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com