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

Adam Sansier via Digitalmars-d digitalmars-d at puremagic.com
Sat Jul 16 23:10:14 PDT 2016


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!












	


More information about the Digitalmars-d mailing list