digitalmars.D - Walter: D asm syntax not consistent nor calling convention with C++
- Adam Sansier (124/124) Jul 16 2016 D is not consistent with C++ in asm and calling convention.
- Adam Sansier (4/6) Jul 16 2016 It would seem to me that that D should default to the pointer
- Walter Bright (6/7) Jul 16 2016 In general, it is a *lot* easier to debug inline asm issues if you're wi...
- Adam Sansier (10/18) Jul 16 2016 I did that. That's been that way from the get go. Are you going
- Adam Sansier (9/21) Jul 16 2016 Oh, if you mean the asm function, then yes, maybe, but that is
- Adam Sansier (7/15) Jul 17 2016 Nevermind, sorry! I didn't mark the callbacks extern(C++)! ;/
- Adam Sansier (9/9) Jul 16 2016 I should point out that the code still doesn't work. It simply
- Walter Bright (5/13) Jul 17 2016 What I suggest is compiling the asm code with C++, and compiling with D....
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!
Jul 16 2016
2. C++ defaults to dword ptr, D to word ptr. Copying and pasting the code results in hard to find bugs.It would seem to me that that D should default to the pointer size for indirect accessing when pushing and popping. push [3] should push a size of void*.sizeof.
Jul 16 2016
On 7/16/2016 11:10 PM, Adam Sansier wrote:D is not consistent with C++ in asm and calling convention.In general, it is a *lot* easier to debug inline asm issues if you're willing to run obj2asm on the output and compare. No need to run a debugger on a crashing program. And you're right that D has a different calling convention than C++. The solution to that is declare the function in the D code as 'extern (C++)'.
Jul 16 2016
On Sunday, 17 July 2016 at 06:23:34 UTC, Walter Bright wrote:On 7/16/2016 11:10 PM, Adam Sansier wrote:I did that. That's been that way from the get go. Are you going to serious take a look at the problem?(If you looked, you would have saw I declared it extern(C++) in the interface). I will send you all the source if you want. All you need is an asio supported sound card or install asio4all and you should be able to run it. Or do you not care if there is a bug in D? I Know it might be my fault but I've spend 2 long days on this problem and haven't gotten anywhere. It's very easy to blow it off, just let me know if that's what you are going to do so I can move on.D is not consistent with C++ in asm and calling convention.In general, it is a *lot* easier to debug inline asm issues if you're willing to run obj2asm on the output and compare. No need to run a debugger on a crashing program. And you're right that D has a different calling convention than C++. The solution to that is declare the function in the D code as 'extern (C++)'.
Jul 16 2016
On Sunday, 17 July 2016 at 06:27:46 UTC, Adam Sansier wrote:On Sunday, 17 July 2016 at 06:23:34 UTC, Walter Bright wrote:Oh, if you mean the asm function, then yes, maybe, but that is besides the point. I created the asm function to deal with the original direct call not working that is calling the interface that is extern(C++). I don't remember if the params were backwards or not but it too crashes. It maybe not have anything to do with the params or whatever but something is totally fried in D. If I simply mark the asm function extern C++ it is even worse.On 7/16/2016 11:10 PM, Adam Sansier wrote:I did that. That's been that way from the get go. Are you goingD is not consistent with C++ in asm and calling convention.In general, it is a *lot* easier to debug inline asm issues if you're willing to run obj2asm on the output and compare. No need to run a debugger on a crashing program. And you're right that D has a different calling convention than C++. The solution to that is declare the function in the D code as 'extern (C++)'.
Jul 16 2016
On Sunday, 17 July 2016 at 06:23:34 UTC, Walter Bright wrote:On 7/16/2016 11:10 PM, Adam Sansier wrote:Nevermind, sorry! I didn't mark the callbacks extern(C++)! ;/ They were being called so it never occurred to me that they were the source of the problem. They were corrupting the stack and creating odd behavior in the driver code. I guess now it makes sense, since the crash happened soon after the callback. D is great! Long live D! ;) Sorry for the noise!D is not consistent with C++ in asm and calling convention.In general, it is a *lot* easier to debug inline asm issues if you're willing to run obj2asm on the output and compare. No need to run a debugger on a crashing program. And you're right that D has a different calling convention than C++. The solution to that is declare the function in the D code as 'extern (C++)'.
Jul 17 2016
I should point out that the code still doesn't work. It simply doesn't crash. It seems to jump to another code location that fails silently. I might not be fixing up the stack frame correctly or or doing something D expects. Hard to no because when I try to step over the `call` instruction it ends up in limbo in another part of the code so I can't check the return state to even see if it passed. I guess EIP is getting thrashed. Regardless, just further proves something is wrong with D as C++ works exactly as it should no matter how the function is called.
Jul 16 2016
On 7/16/2016 11:24 PM, Adam Sansier wrote:I should point out that the code still doesn't work. It simply doesn't crash. It seems to jump to another code location that fails silently. I might not be fixing up the stack frame correctly or or doing something D expects. Hard to no because when I try to step over the `call` instruction it ends up in limbo in another part of the code so I can't check the return state to even see if it passed. I guess EIP is getting thrashed. Regardless, just further proves something is wrong with D as C++ works exactly as it should no matter how the function is called.What I suggest is compiling the asm code with C++, and compiling with D. Run obj2asm on the output, and compare. Since you need them to produce the same result, any differences should be obvious. It's much easier than using a debugger.
Jul 17 2016