www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Walter: D asm syntax not consistent nor calling convention with C++

reply Adam Sansier <Adam.Sansier gmail.com> writes:
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
next sibling parent Adam Sansier <Adam.Sansier gmail.com> writes:
 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
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Sunday, 17 July 2016 at 06:23:34 UTC, Walter Bright wrote:
 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++)'.
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.
Jul 16 2016
parent Adam Sansier <Adam.Sansier gmail.com> writes:
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:
 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++)'.
I did that. That's been that way from the get go. Are you going
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.
Jul 16 2016
prev sibling parent Adam Sansier <Adam.Sansier gmail.com> writes:
On Sunday, 17 July 2016 at 06:23:34 UTC, Walter Bright wrote:
 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++)'.
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!
Jul 17 2016
prev sibling parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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