【问题标题】:Strange Clang behaviour奇怪的 Clang 行为
【发布时间】:2013-03-25 18:37:26
【问题描述】:

看看这段代码:

#include <iostream>
#include <string>

void foo(int(*f)()) {
    std::cout << f() << std::endl;
}

void foo(std::string(*f)()) {
    std::string s = f();
    std::cout << s << std::endl;
}

int main() {
    auto bar = [] () -> std::string {
        return std::string("bla");
    };

    foo(bar);

    return 0;
}

编译

g++ -o test test.cpp -std=c++11

导致:

bla

就像它应该做的那样。用

编译它
clang++ -o test test.cpp -std=c++11 -stdlib=libc++

导致:

zsh: illegal hardware instruction  ./test

并用它编译

clang++ -o test test.cpp -std=c++11 -stdlib=stdlibc++

还导致:

zsh: illegal hardware instruction  ./test

Clang/GCC 版本:

clang version 3.2 (tags/RELEASE_32/final)
Target: x86_64-pc-linux-gnu
Thread model: posix

gcc version 4.7.2 (Gentoo 4.7.2-r1 p1.5, pie-0.5.5) 

任何人有什么建议是怎么回事?

提前致谢!

【问题讨论】:

  • 我会说这是 Clang 中的一个错误
  • 仅供参考,有关ud2 and clang的更多信息

标签: c++ c++11 g++ llvm clang


【解决方案1】:

是的,这是 Clang++ 中的一个错误。我可以在 i386-pc-linux-gnu 中使用 CLang 3.2 重现它。

现在进行一些随机分析...

我发现错误在于从 labmda 到指针函数的转换:编译器创建了一种 thunk 具有调用 lambda 的适当签名,但它具有指令ud2 而不是ret

您可能都知道,指令ud2 是一条明确引发“无效操作码”异常的指令。也就是说,一条指令故意未定义。

看看反汇编:这是thunk函数:

main::$_0::__invoke():
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        movl    %ecx, 4(%esp)
        calll   main::$_0::operator()() const ; this calls to the real lambda
        subl    $4, %esp
        ud2   ; <<<-- What the...!!!

因此,该错误的一个最小示例将是:

int main() {
    std::string(*f)() = [] () -> std::string {
        return "bla";
    };
    f();
    return 0;
}

奇怪的是,如果返回类型是简单类型,例如int,则不会发生该错误。那么生成的thunk就是:

main::$_0::__invoke():
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    %eax, (%esp)
        calll   main::$_0::operator()() const
        addl    $8, %esp
        popl    %ebp
        ret

我怀疑问题出在返回值的转发上。如果它适合一个寄存器,例如eax,一切顺利。但是如果是一个很大的struct,比如std::string,在栈中返回,编译器就会一头雾水,绝望地发出ud2

【讨论】:

    【解决方案2】:

    这很可能是 clang 3.2 中的一个错误。我无法使用 clang trunk 重现崩溃。

    【讨论】:

    • 肯定的。树干似乎工作正常。所以它可能应该在clang 3.3中修复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多