rralf
rralf

Reputation: 1252

Strange Clang behaviour

Have a look at this piece of code:

#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;
}

Compiling it with

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

leads to:

bla

like it should do. Compiling it with

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

leads to:

zsh: illegal hardware instruction  ./test

And Compiling it with

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

leads also to:

zsh: illegal hardware instruction  ./test

Clang/GCC Versions:

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) 

Anyone any suggestions what is going wrong?

Thanks in advance!

Upvotes: 14

Views: 1150

Answers (2)

rodrigo
rodrigo

Reputation: 98328

Yes, it is a bug in Clang++. I can reproduce it with CLang 3.2 in i386-pc-linux-gnu.

And now some random analysis...

I've found that the bug is in the conversion from labmda to pointer-to-function: the compiler creates a kind of thunk with the appropriate signature that calls the lambda, but it has the instruction ud2 instead of ret.

The instruction ud2, as you all probably know, is an instruction that explicitly raises the "Invalid Opcode" exception. That is, an instruction intentionally left undefined.

Take a look at the disassemble: this is the thunk function:

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...!!!

So a minimal example of the bug will be simply:

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

Curiously enough, the bug doesn't happen if the return type is a simple type, such as int. Then the generated thunk is:

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

I suspect that the problem is in the forwarding of the return value. If it fits in a register, such as eax all goes well. But if it is a big struct, such as std::string it is returned in the stack, the compiler is confused and emits the ud2 in desperation.

Upvotes: 11

d0k
d0k

Reputation: 2580

This is most likely a bug in clang 3.2. I can't reproduce the crash with clang trunk.

Upvotes: 5

Related Questions