www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Payload structure problem using inline asm

reply "Baz" <burg.basile yahoo.com> writes:
Hello, I've defined a simple template used in a double linked 
list implementation:

template tDLListItem(T)
{
	const cPrevOffs = size_t.sizeof;
	const cNextOffs = size_t.sizeof + size_t.sizeof;
	void* NewItemCaps(T* aData, void* aPrevious, void* aNext)
	{
		auto lPt = std.c.stdlib.malloc( 3 * size_t.sizeof );
		if (!lPt)
		{
			throw new OutOfMemoryError();
		}
		*cast(size_t*)  lPt = cast(size_t) aData;
		*cast(size_t*) (lPt + cPrevOffs) = cast(size_t) aPrevious;
		*cast(size_t*) (lPt + cNextOffs) = cast(size_t) aNext;
		return lPt;
	}
	void DeleteItemCaps(void* aItemCaps)
	{
		std.c.stdlib.free(aItemCaps);
	}
	void SetItemCapsPrev(void* aItemCaps, void* aPrevious)
	{
		*cast(size_t*) (aItemCaps + cPrevOffs) = cast(size_t) aPrevious;
	}
	void SetItemCapsNext(void* aItemCaps, void* aNext)
	{
		*cast(size_t*) (aItemCaps + cNextOffs) = cast(size_t) aNext;
	}
	void SetItemCapsData(void* aItemCaps, T* aData)
	{
		*cast(size_t*) aItemCaps = cast(size_t) aData;
	}
	T* GetItemCapsData(void* aItemCaps)
	{
		version(X86) asm
		{
			naked;
			mov     EAX, [EAX];
			ret;
		}
		else version(none) asm
		{
			naked;
			mov     RAX, [RAX];
			ret;
		}
		else
		{
			return *cast(T**) (aItemCaps);
		}
	}
	void* PreviousItemCaps(void* aItemCaps)
	{
		version(X86) asm
		{
			naked;
			mov     EAX, [EAX + cPrevOffs];
			ret;
		}
		else version(none) asm
		{
			naked;
			mov     RAX, [RAX + cPrevOffs];
			ret;
		}
		else
		{
			return *cast(size_t**) (aItemCaps + cPrevOffs);
		}
	}
	void* NextItemCaps(void* aItemCaps)
	{
		version(X86) asm
		{
			naked;
			mov     EAX, [EAX + cNextOffs];
			ret;
		}
		else version(none) asm
		{
			naked;
			mov     RAX, [RAX + cNextOffs];
			ret;
		}
		else
		{
			return *cast(size_t**) (aItemCaps + cNextOffs);
		}
	}
}

It's used as a struct. (BTW I call the "payload" a "capsule", a 
"caps"). In x86 (tested on win) I can optimize the access into 
the "capsule" to a simple member (I return the data pointed by 
the parameter by digging using the param + memberoffset as an 
address...

But it doesn't work in x86_64 (tested on nux). What's wrong with 
that ? Does I miss something in the parameters convention/ABI for 
x64 (that's why the x64 versions are surounded by version(none) 
instead of version (X86_64) )) ?

Does dmd produce real x86_64 code or is it possible for a 64 bit 
appli to work with 32 bit pointers ?
(I know this Q coulds look weird but I've already seen some false 
x86_64 while doing some static analysis)

WTF.MEH.
Jul 17 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 17 July 2013 at 16:52:40 UTC, Baz wrote:
 Hello, I've defined a simple template used in a double linked 
 list implementation:

 template tDLListItem(T)
 {
 	const cPrevOffs = size_t.sizeof;
 	const cNextOffs = size_t.sizeof + size_t.sizeof;
 	void* NewItemCaps(T* aData, void* aPrevious, void* aNext)
 	{
 		auto lPt = std.c.stdlib.malloc( 3 * size_t.sizeof );
 		if (!lPt)
 		{
 			throw new OutOfMemoryError();
 		}
 		*cast(size_t*)  lPt = cast(size_t) aData;
 		*cast(size_t*) (lPt + cPrevOffs) = cast(size_t) aPrevious;
 		*cast(size_t*) (lPt + cNextOffs) = cast(size_t) aNext;
 		return lPt;
 	}
 	void DeleteItemCaps(void* aItemCaps)
 	{
 		std.c.stdlib.free(aItemCaps);
 	}
 	void SetItemCapsPrev(void* aItemCaps, void* aPrevious)
 	{
 		*cast(size_t*) (aItemCaps + cPrevOffs) = cast(size_t) 
 aPrevious;
 	}
 	void SetItemCapsNext(void* aItemCaps, void* aNext)
 	{
 		*cast(size_t*) (aItemCaps + cNextOffs) = cast(size_t) aNext;
 	}
 	void SetItemCapsData(void* aItemCaps, T* aData)
 	{
 		*cast(size_t*) aItemCaps = cast(size_t) aData;
 	}
 	T* GetItemCapsData(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX];
 			ret;
 		}
 		else
 		{
 			return *cast(T**) (aItemCaps);
 		}
 	}
 	void* PreviousItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cPrevOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cPrevOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cPrevOffs);
 		}
 	}
 	void* NextItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cNextOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cNextOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cNextOffs);
 		}
 	}
 }

 It's used as a struct. (BTW I call the "payload" a "capsule", a 
 "caps"). In x86 (tested on win) I can optimize the access into 
 the "capsule" to a simple member (I return the data pointed by 
 the parameter by digging using the param + memberoffset as an 
 address...

 But it doesn't work in x86_64 (tested on nux). What's wrong 
 with that ? Does I miss something in the parameters 
 convention/ABI for x64 (that's why the x64 versions are 
 surounded by version(none) instead of version (X86_64) )) ?

 Does dmd produce real x86_64 code or is it possible for a 64 
 bit appli to work with 32 bit pointers ?
 (I know this Q coulds look weird but I've already seen some 
 false x86_64 while doing some static analysis)

 WTF.MEH.
AFAIK on windows dmd uses the optlink calling convention, so the 1st argument is in EAX On linux 32bit (cdecl) the first argument will be on the top of the stack. On linux 64bit (system V) the first argument will be in RDX see: http://en.wikipedia.org/wiki/X86_calling_conventions or find a copy of the system V abi docs somewhere.
Jul 17 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 17 July 2013 at 17:09:30 UTC, John Colvin wrote:
 On Wednesday, 17 July 2013 at 16:52:40 UTC, Baz wrote:
 Hello, I've defined a simple template used in a double linked 
 list implementation:

 template tDLListItem(T)
 {
 	const cPrevOffs = size_t.sizeof;
 	const cNextOffs = size_t.sizeof + size_t.sizeof;
 	void* NewItemCaps(T* aData, void* aPrevious, void* aNext)
 	{
 		auto lPt = std.c.stdlib.malloc( 3 * size_t.sizeof );
 		if (!lPt)
 		{
 			throw new OutOfMemoryError();
 		}
 		*cast(size_t*)  lPt = cast(size_t) aData;
 		*cast(size_t*) (lPt + cPrevOffs) = cast(size_t) aPrevious;
 		*cast(size_t*) (lPt + cNextOffs) = cast(size_t) aNext;
 		return lPt;
 	}
 	void DeleteItemCaps(void* aItemCaps)
 	{
 		std.c.stdlib.free(aItemCaps);
 	}
 	void SetItemCapsPrev(void* aItemCaps, void* aPrevious)
 	{
 		*cast(size_t*) (aItemCaps + cPrevOffs) = cast(size_t) 
 aPrevious;
 	}
 	void SetItemCapsNext(void* aItemCaps, void* aNext)
 	{
 		*cast(size_t*) (aItemCaps + cNextOffs) = cast(size_t) aNext;
 	}
 	void SetItemCapsData(void* aItemCaps, T* aData)
 	{
 		*cast(size_t*) aItemCaps = cast(size_t) aData;
 	}
 	T* GetItemCapsData(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX];
 			ret;
 		}
 		else
 		{
 			return *cast(T**) (aItemCaps);
 		}
 	}
 	void* PreviousItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cPrevOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cPrevOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cPrevOffs);
 		}
 	}
 	void* NextItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cNextOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cNextOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cNextOffs);
 		}
 	}
 }

 It's used as a struct. (BTW I call the "payload" a "capsule", 
 a "caps"). In x86 (tested on win) I can optimize the access 
 into the "capsule" to a simple member (I return the data 
 pointed by the parameter by digging using the param + 
 memberoffset as an address...

 But it doesn't work in x86_64 (tested on nux). What's wrong 
 with that ? Does I miss something in the parameters 
 convention/ABI for x64 (that's why the x64 versions are 
 surounded by version(none) instead of version (X86_64) )) ?

 Does dmd produce real x86_64 code or is it possible for a 64 
 bit appli to work with 32 bit pointers ?
 (I know this Q coulds look weird but I've already seen some 
 false x86_64 while doing some static analysis)

 WTF.MEH.
AFAIK on windows dmd uses the optlink calling convention, so the 1st argument is in EAX On linux 32bit (cdecl) the first argument will be on the top of the stack. On linux 64bit (system V) the first argument will be in RDX see: http://en.wikipedia.org/wiki/X86_calling_conventions or find a copy of the system V abi docs somewhere.
big woops there, linux x64 arguments are RDI first, not RDX The order is RDI RSI RDX RCX R8 R9
Jul 17 2013
parent reply "Baz" <burg.basile yahoo.com> writes:
On Wednesday, 17 July 2013 at 17:11:20 UTC, John Colvin wrote:
 On Wednesday, 17 July 2013 at 17:09:30 UTC, John Colvin wrote:
 On Wednesday, 17 July 2013 at 16:52:40 UTC, Baz wrote:
 Hello, I've defined a simple template used in a double linked 
 list implementation:

 template tDLListItem(T)
 {
 	const cPrevOffs = size_t.sizeof;
 	const cNextOffs = size_t.sizeof + size_t.sizeof;
 	void* NewItemCaps(T* aData, void* aPrevious, void* aNext)
 	{
 		auto lPt = std.c.stdlib.malloc( 3 * size_t.sizeof );
 		if (!lPt)
 		{
 			throw new OutOfMemoryError();
 		}
 		*cast(size_t*)  lPt = cast(size_t) aData;
 		*cast(size_t*) (lPt + cPrevOffs) = cast(size_t) aPrevious;
 		*cast(size_t*) (lPt + cNextOffs) = cast(size_t) aNext;
 		return lPt;
 	}
 	void DeleteItemCaps(void* aItemCaps)
 	{
 		std.c.stdlib.free(aItemCaps);
 	}
 	void SetItemCapsPrev(void* aItemCaps, void* aPrevious)
 	{
 		*cast(size_t*) (aItemCaps + cPrevOffs) = cast(size_t) 
 aPrevious;
 	}
 	void SetItemCapsNext(void* aItemCaps, void* aNext)
 	{
 		*cast(size_t*) (aItemCaps + cNextOffs) = cast(size_t) aNext;
 	}
 	void SetItemCapsData(void* aItemCaps, T* aData)
 	{
 		*cast(size_t*) aItemCaps = cast(size_t) aData;
 	}
 	T* GetItemCapsData(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX];
 			ret;
 		}
 		else
 		{
 			return *cast(T**) (aItemCaps);
 		}
 	}
 	void* PreviousItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cPrevOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cPrevOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cPrevOffs);
 		}
 	}
 	void* NextItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cNextOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cNextOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cNextOffs);
 		}
 	}
 }

 It's used as a struct. (BTW I call the "payload" a "capsule", 
 a "caps"). In x86 (tested on win) I can optimize the access 
 into the "capsule" to a simple member (I return the data 
 pointed by the parameter by digging using the param + 
 memberoffset as an address...

 But it doesn't work in x86_64 (tested on nux). What's wrong 
 with that ? Does I miss something in the parameters 
 convention/ABI for x64 (that's why the x64 versions are 
 surounded by version(none) instead of version (X86_64) )) ?

 Does dmd produce real x86_64 code or is it possible for a 64 
 bit appli to work with 32 bit pointers ?
 (I know this Q coulds look weird but I've already seen some 
 false x86_64 while doing some static analysis)

 WTF.MEH.
AFAIK on windows dmd uses the optlink calling convention, so the 1st argument is in EAX On linux 32bit (cdecl) the first argument will be on the top of the stack. On linux 64bit (system V) the first argument will be in RDX see: http://en.wikipedia.org/wiki/X86_calling_conventions or find a copy of the system V abi docs somewhere.
big woops there, linux x64 arguments are RDI first, not RDX The order is RDI RSI RDX RCX R8 R9
Thx. I'll test that. My initial x86_64 implementation was based on a wrong assumption...(that rax is used as eax for parameters...).
Jul 17 2013
parent "Baz" <burg.basile yahoo.com> writes:
On Wednesday, 17 July 2013 at 18:04:38 UTC, Baz wrote:
 On Wednesday, 17 July 2013 at 17:11:20 UTC, John Colvin wrote:
 On Wednesday, 17 July 2013 at 17:09:30 UTC, John Colvin wrote:
 On Wednesday, 17 July 2013 at 16:52:40 UTC, Baz wrote:
 Hello, I've defined a simple template used in a double 
 linked list implementation:

 template tDLListItem(T)
 {
 	const cPrevOffs = size_t.sizeof;
 	const cNextOffs = size_t.sizeof + size_t.sizeof;
 	void* NewItemCaps(T* aData, void* aPrevious, void* aNext)
 	{
 		auto lPt = std.c.stdlib.malloc( 3 * size_t.sizeof );
 		if (!lPt)
 		{
 			throw new OutOfMemoryError();
 		}
 		*cast(size_t*)  lPt = cast(size_t) aData;
 		*cast(size_t*) (lPt + cPrevOffs) = cast(size_t) aPrevious;
 		*cast(size_t*) (lPt + cNextOffs) = cast(size_t) aNext;
 		return lPt;
 	}
 	void DeleteItemCaps(void* aItemCaps)
 	{
 		std.c.stdlib.free(aItemCaps);
 	}
 	void SetItemCapsPrev(void* aItemCaps, void* aPrevious)
 	{
 		*cast(size_t*) (aItemCaps + cPrevOffs) = cast(size_t) 
 aPrevious;
 	}
 	void SetItemCapsNext(void* aItemCaps, void* aNext)
 	{
 		*cast(size_t*) (aItemCaps + cNextOffs) = cast(size_t) 
 aNext;
 	}
 	void SetItemCapsData(void* aItemCaps, T* aData)
 	{
 		*cast(size_t*) aItemCaps = cast(size_t) aData;
 	}
 	T* GetItemCapsData(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX];
 			ret;
 		}
 		else
 		{
 			return *cast(T**) (aItemCaps);
 		}
 	}
 	void* PreviousItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cPrevOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cPrevOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cPrevOffs);
 		}
 	}
 	void* NextItemCaps(void* aItemCaps)
 	{
 		version(X86) asm
 		{
 			naked;
 			mov     EAX, [EAX + cNextOffs];
 			ret;
 		}
 		else version(none) asm
 		{
 			naked;
 			mov     RAX, [RAX + cNextOffs];
 			ret;
 		}
 		else
 		{
 			return *cast(size_t**) (aItemCaps + cNextOffs);
 		}
 	}
 }

 It's used as a struct. (BTW I call the "payload" a 
 "capsule", a "caps"). In x86 (tested on win) I can optimize 
 the access into the "capsule" to a simple member (I return 
 the data pointed by the parameter by digging using the param 
 + memberoffset as an address...

 But it doesn't work in x86_64 (tested on nux). What's wrong 
 with that ? Does I miss something in the parameters 
 convention/ABI for x64 (that's why the x64 versions are 
 surounded by version(none) instead of version (X86_64) )) ?

 Does dmd produce real x86_64 code or is it possible for a 64 
 bit appli to work with 32 bit pointers ?
 (I know this Q coulds look weird but I've already seen some 
 false x86_64 while doing some static analysis)

 WTF.MEH.
AFAIK on windows dmd uses the optlink calling convention, so the 1st argument is in EAX On linux 32bit (cdecl) the first argument will be on the top of the stack. On linux 64bit (system V) the first argument will be in RDX see: http://en.wikipedia.org/wiki/X86_calling_conventions or find a copy of the system V abi docs somewhere.
big woops there, linux x64 arguments are RDI first, not RDX The order is RDI RSI RDX RCX R8 R9
Thx. I'll test that. My initial x86_64 implementation was based on a wrong assumption...(that rax is used as eax for parameters...).
Thx again, I've finished with this: T* GetItemCapsData(void* aItemCaps) { version(Win32) asm { naked; mov EAX, [EAX]; ret; } else version(Win64) asm { naked; mov RAX, [RCX]; ret; } else version(linux64)asm { naked; mov RAX, [RDI]; ret; } else { return *cast(T**) (aItemCaps); } } now I'll decompile the -release -inline -O version compiled without the inline asm and will realize that dmd generates the same, but without any CALL... BTW, it would be great if version() could use logical operators. instead of a custom definition: version (linux) version (X86_64) version = linux64; something like: version(linux & X86_64){}
Jul 18 2013