在NDK和clang++中针对ARM的编译器错误?

7
请看下面的代码:
float test(int len, int* tab)
{
    for(int i = 0; i<len; i++)
        tab[i] = i;
}

很明显缺少返回值。在这种情况下,对于ARM处理器的clang和ndk编译器都会生成一个无限循环。反汇编后可以看出编译器生成的是普通分支指令而非条件分支。
    mov     r0, #0
.LBB0_1:
    str     r0, [r1, r0, lsl #2]
    add     r0, r0, #1
    b       .LBB0_1

这里可以找到一个出错的示例: https://godbolt.org/z/YDSFw-

请注意,C++规范说明缺少返回值被视为未定义行为,但仅涉及返回值。它不应影响前面的指令。

我有遗漏什么吗?有什么想法吗?


3
你需要翻译的内容是:“它仅涉及返回值。它不应影响先前的指令。”你在看哪个条款?9.6.3中的第二部分“返回语句”指出:“否则,从函数(main除外)(6.8.3.1)的末尾流出会导致未定义行为”。对我来说,这并没有说明未定义性只适用于返回值是什么。 - Michael
1个回答

3
不,你不能用未定义的行为这种方式进行推理。编译器可以自由地使用未定义的行为和相关假设进行优化。编译器可以假设你的代码不包含未定义的行为。在这种情况下,编译器可以假设具有未定义行为的代码将不会被执行。由于函数末尾包含未定义行为,编译器认为函数末尾实际上永远不会被执行,因此可以优化循环。如果您删除-Oz并向编译器资源管理器命令添加-emit-llvm,则可以查看LLVM IR clang最初生成的内容,而不进行优化:https://godbolt.org/z/-dbeNj
define dso_local float @_Z4testiPi(i32 %0, i32* %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32*, align 4
  %5 = alloca i32, align 4
  store i32 %0, i32* %3, align 4
  store i32* %1, i32** %4, align 4
  store i32 0, i32* %5, align 4
  br label %6

6:                                                ; preds = %15, %2
  %7 = load i32, i32* %5, align 4
  %8 = load i32, i32* %3, align 4
  %9 = icmp slt i32 %7, %8
  br i1 %9, label %10, label %18

10:                                               ; preds = %6
  %11 = load i32, i32* %5, align 4
  %12 = load i32*, i32** %4, align 4
  %13 = load i32, i32* %5, align 4
  %14 = getelementptr inbounds i32, i32* %12, i32 %13
  store i32 %11, i32* %14, align 4
  br label %15

15:                                               ; preds = %10
  %16 = load i32, i32* %5, align 4
  %17 = add nsw i32 %16, 1
  store i32 %17, i32* %5, align 4
  br label %6

18:                                               ; preds = %6
  call void @llvm.trap()
  unreachable
}

循环的结束标签18处含有“unreachable”。这可以用于进一步优化,消除循环开始处的分支和比较。
编辑: John Regehr有一篇关于如何推理C和C++中未定义行为的优秀博客文章。它有点长,但值得一读。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接