• lambda calling-conventions

    From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Fri Feb 26 15:17:05 2021
    From Newsgroup: comp.lang.c++

    Lambdas with no captures can be handled like ordinary functions and you
    can get the address of the lambda as a normal function-pointer. AFAIK
    there's no compiler which allows to specify a callig-convention on a
    lambda. But with MSVC you can convert a lamda to a function-pointer of
    any type, i.e. you can convert it to __cdecl, __fastcall or whatever
    (they exist for x86 only, x64 has a consistent calling convention for
    all calls). What MSVC does here is that it compiles the lambda multiple
    times for each calling convention on demand.

    Look here:

    #include <iostream>
    #include <string>
    #include <iomanip>
    #include <cstdint>

    using namespace std;

    void fastCall( void (__fastcall *pf)( string ) )
    {
    string str = "hello world";
    cout << p << endl;
    pf( str );
    }

    void cdeclCall( void (__cdecl *pf)( string ) )
    {
    string str = "hello world";
    cout << p << endl;
    pf( str );
    }

    int main()
    {
    auto lambda = []( string str )
    {
    cout << str << endl;
    };
    fastCall( lambda );
    cdeclCall( lambda );
    }

    As you can see the addresses pf vary. You might think that the compiler generates proxy-code and the string is copy-constructed with each proxy.
    But I disassembled the lambda and found that the lambda is compiled
    completely multiple times.

    So are there any other compilers which have different calling conven-
    tions and which do the same ?
    --- Synchronet 3.18c-Linux NewsLink 1.113
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Fri Feb 26 17:08:15 2021
    From Newsgroup: comp.lang.c++

    On 26/02/2021 15:17, Bonita Montero wrote:
    Lambdas with no captures can be handled like ordinary functions and you
    can get the address of the lambda as a normal function-pointer. AFAIK
    there's no compiler which allows to specify a callig-convention on a
    lambda. But with MSVC you can convert a lamda to a function-pointer of
    any type, i.e. you can convert it to __cdecl, __fastcall or whatever
    (they exist for x86 only, x64 has a consistent calling convention for
    all calls). What MSVC does here is that it compiles the lambda multiple
    times for each calling convention on demand.


    Calling conventions - especially the various ones supported by MSVC -
    are very much implementation specific. On most platforms you have a
    single ABI that all compilers adhere to, but not on 32-bit Windows. So
    how different calling conventions are handled on Win32 will be entirely
    up to the compiler.

    Lambdas have internal linkage and are usually not "exported" in any way,
    so compilers are free to use whatever calling conventions they want -
    there is no need for any consistency, especially if the lambda is a leaf function.

    When you convert a lambda to a pointer-to-function, you are actually
    using a conversion function template which is free to give different
    results for different pointer types. (It can even give different
    results for the same pointer types, although that would be less likely
    in a real implementation.)

    See the section on "ClosureType::operator ret(*)(params)()" :

    <https://en.cppreference.com/w/cpp/language/lambda>


    Of curiosity, what happens if you replace the lambda with a static function:

    static auto lambda(string str) { cout << str << endl; }

    ?


    --- Synchronet 3.18c-Linux NewsLink 1.113
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Fri Feb 26 17:21:44 2021
    From Newsgroup: comp.lang.c++

    Calling conventions - especially the various ones supported by MSVC -
    are very much implementation specific. On most platforms you have a
    single ABI that all compilers adhere to, ...

    On Windows x64 there's no such thing as __cdecl, __fastcall, __stdcall
    etc. anymore.
    --- Synchronet 3.18c-Linux NewsLink 1.113
  • From David Brown@david.brown@hesbynett.no to comp.lang.c++ on Fri Feb 26 17:41:18 2021
    From Newsgroup: comp.lang.c++

    On 26/02/2021 17:21, Bonita Montero wrote:
    Calling conventions - especially the various ones supported by MSVC -
    are very much implementation specific.  On most platforms you have a
    single ABI that all compilers adhere to, ...

    On Windows x64 there's no such thing as __cdecl, __fastcall, __stdcall
    etc. anymore.

    Well, MS didn't do nearly as badly for Win64 as they did for Win32
    (where there are half a dozen conventions) or DOS (where there is so
    little defined that it's a stretch to use the word "convention"). They
    tried to have a single ABI - though in good old MS fashion, they saw
    that everyone else had settled on a single consistent ABI and thus
    decided they needed a completely different one instead. But there is
    still the "normal" MS Win64 calling convention, and the "__vectorcall"
    version.

    However, I thought you were specifically talking about Win32 since you
    were talking about __cdecl, __fastcall, etc., ?

    --- Synchronet 3.18c-Linux NewsLink 1.113
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Fri Feb 26 17:43:45 2021
    From Newsgroup: comp.lang.c++

    Well, MS didn't do nearly as badly for Win64 as they did for Win32
    (where there are half a dozen conventions) or DOS (where there is so
    little defined that it's a stretch to use the word "convention"). They
    tried to have a single ABI - though in good old MS fashion, they saw
    that everyone else had settled on a single consistent ABI and thus
    decided they needed a completely different one instead. But there is
    still the "normal" MS Win64 calling convention, and the "__vectorcall" version.

    With most other platforms there's usually a default calling convention
    and mostly a cdecl calling convention which pushes everything on the
    stack.

    --- Synchronet 3.18c-Linux NewsLink 1.113
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Fri Feb 26 17:48:25 2021
    From Newsgroup: comp.lang.c++

    Am 26.02.2021 um 15:17 schrieb Bonita Montero:
    Lambdas with no captures can be handled like ordinary functions and you
    can get the address of the lambda as a normal function-pointer. AFAIK
    there's no compiler which allows to specify a callig-convention on a
    lambda. But with MSVC you can convert a lamda to a function-pointer of
    any type, i.e. you can convert it to __cdecl, __fastcall or whatever
    (they exist for x86 only, x64 has a consistent calling convention for
    all calls). What MSVC does here is that it compiles the lambda multiple
    times for each calling convention on demand.

    Look here:

    #include <iostream>
    #include <string>
    #include <iomanip>
    #include <cstdint>

    using namespace std;

    void fastCall( void (__fastcall *pf)( string ) )
    {
        string str = "hello world";
        cout << p << endl;
    pf, not p
        pf( str );
    }

    void cdeclCall( void (__cdecl *pf)( string ) )
    {
        string str = "hello world";
        cout << p << endl;
    pf, not p
        pf( str );
    }

    int main()
    {
        auto lambda = []( string str )
        {
            cout << str << endl;
        };
        fastCall( lambda );
        cdeclCall( lambda );
    }

    As you can see the addresses pf vary. You might think that the compiler generates proxy-code and the string is copy-constructed with each proxy.
    But I disassembled the lambda and found that the lambda is compiled completely multiple times.

    So are there any other compilers which have different calling conven-
    tions and which do the same ?

    --- Synchronet 3.18c-Linux NewsLink 1.113
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Fri Feb 26 17:26:28 2021
    From Newsgroup: comp.lang.c++

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Well, MS didn't do nearly as badly for Win64 as they did for Win32
    (where there are half a dozen conventions) or DOS (where there is so
    little defined that it's a stretch to use the word "convention"). They
    tried to have a single ABI - though in good old MS fashion, they saw
    that everyone else had settled on a single consistent ABI and thus
    decided they needed a completely different one instead. But there is
    still the "normal" MS Win64 calling convention, and the "__vectorcall"
    version.

    With most other platforms there's usually a default calling convention
    and mostly a cdecl calling convention which pushes everything on the
    stack.

    Most platforms I've seen in the past two decades use calling conventions
    that pass the first N parameters in registers, where N varies based
    on the number of registers. E.g. x86_64 on unix/linux (N=6), Motorola 88100, IA64, et alia.

    Calling conventions also agree on how to handle varargs and how to pass
    SIMD, vector (and for ARM, scalable vector) values.
    --- Synchronet 3.18c-Linux NewsLink 1.113