D is not consistent with C++ in asm and calling convention.

I have the exact same code in both C++ and D, the exact same assembler code, but.


D's

                        enum VS = (void*).sizeof;
                        enum SA = 6;
                        enum OF = 0;
void createBuffers(iASIO a, void* funcptr, void *buf, int num, int size, void* callbacks)
                        {               
                                asm
                                {
                                        naked;
                                        enter 0 * 4, 0;
                

push dword ptr [EBP + VS + SA*VS - OF*VS - 0*VS]; // Callbacks push dword ptr [EBP + VS + SA*VS - OF*VS - 1*VS]; // bufferSize push dword ptr [EBP + VS + SA*VS - OF*VS - 2*VS]; // numChannels push dword ptr [EBP + VS + SA*VS - OF*VS - 3*VS]; // bufferInfos


                                        call dword ptr [EBP + VS + SA*VS - 
OF*VS - 4*VS]; // call fnc
                                        leave;
                                        ret;

                                }                       
                        }



C's:


#define SA 6
#define VS 4
#define OF 0
__declspec(naked) ASIOError ASIOCreateBuffers(void* ptr, void* fnc, ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks)
{
        
        __asm
        {       
                enter 0 * 4, 0;
                

                push [EBP + VS + SA*VS - OF*VS - 0*VS]; // Callbacks
                push [EBP + VS + SA*VS - OF*VS - 1*VS]; // bufferSize
                push [EBP + VS + SA*VS - OF*VS - 2*VS]; // numChannels
                push [EBP + VS + SA*VS - OF*VS - 3*VS]; // bufferInfos


                call [EBP + VS + SA*VS - OF*VS - 4*VS]; // call fnc
                leave;
                ret;
        }
}


1. C++ allows lower case, D does not. This is somewhat annoying but not a big deal. Strange that one would mark the block naked by not the function.

2. C++ defaults to dword ptr, D to word ptr. Copying and pasting the code results in hard to find bugs.

3. D does not allow () in offset calculation. Notice the verbosity simply because parenthesis are not allowed.




---------------------------------
**4**. D's calling convention is different than C++'s. I'm not sure what is going on. Major source of headaches.
---------------------------------




Also, all this is because D crashes when trying to simply call the function directly while C++ works fine: All the arg values are correct and exactly the same as verified by comparing memory values.

C++'s stack frame: 00d1ff3c 00000014 000000c0 00d210f8
D's Stack frame: 0038c258 000000c0 00000014 003972f8

They are reversed!

The function is marked marked extern(C++) but extern(C) and extern(Windows) doesn't change the order either.

Surely there is something wrong with the calling convention? This is COM Stuff. I copied the interface right out of the C++ version and added extern(C++)(crashes with extern(Windows)).

// The Asio COM interface
extern(C++) interface iASIO : IUnknown
{
extern(C++){
...
ASIOError createBuffers(sASIOBufferInfo *bufferInfos, size_t numChannels, size_t bufferSize, sASIOCallbacks *callbacks);// 19
...
}
}


Hopefully you can spend 20 mins thinking about this as I've spent 2 days trying to figure out WTF is going on here. Why? Reversing the parameters doesn't solve the problem, it eventually crashes. The function does get the correct values as one can see the memory change for the buffers and such.

The magic?

D Version:

asm
                                {
                                        naked;
                                        enter 0 * 4, 0;
                
                                        
push dword ptr [EBP + VS + SA*VS - OF*VS - 3*VS]; // bufferInfos push dword ptr [EBP + VS + SA*VS - OF*VS - 2*VS]; // numChannels push dword ptr [EBP + VS + SA*VS - OF*VS - 1*VS]; // bufferSize push dword ptr [EBP + VS + SA*VS - OF*VS - 0*VS]; // Callbacks
                                        push dword ptr [EBP + VS + SA*VS - 
OF*VS - 5*VS];       // this

                                        call dword ptr [EBP + VS + SA*VS - 
OF*VS - 4*VS]; // call fnc
                                        leave;
                                        ret;

                                }

Push `this` on the stack and it works! Notice the order though! Not what one would expect. It's as if D is passing `this` last instead of first!



Hence I had to go from

//auto res = theAsioDriver->createBuffers(bufferInfos, numChannels, bufferSize, callbacks); return res;

To all that assembly just to figure it all out. It seems there is a bug in the calling convention somewhere, or that D needs another calling convention. Im going to have to wrap every interface function with this sort of mess both for x86 and x64.

BTW, why does D use enter/leave when it is slower than push/pop?

This has been a very frustrating experience with D! Maybe it's just one of those illusive `kick your ass bugs`, it sure kicked mine!












        

Reply via email to