Help
cancel
Showing results for 
Search instead for 
Did you mean: 
Copilot Lvl 2
Message 1 of 6

What's that horrible pointer check in the gcc's generated code?

Hallo! Anybody can explain me what's this strange checks the GCC compiler adds to my code?...
Here is all the source files of my test application demonstrating the high perfomance (as I planed) "cyclefied" single linked list's implementation:

Spoiler
/*
 * File:	mylist.h
*/

#ifndef __mylist_H__
#define __mylist_H__

#ifndef INLINE
#ifdef __GNUC__
#define INLINE __attribute__ ((always_inline)) inline
#else
#define INLINE inline
#endif //__GNUC__
#endif //INLINE


/*
 * Single linked list node class
 */

class CSListNode
{
private:
	union
	{
		CSListNode* pFirstNode;
		CSListNode* pNextNode;
	};
	friend class CSList;

public:

	INLINE CSListNode* GetNextNode() {return pNextNode;}
};


/*
 * Single linked list class
 */

class CSList: private CSListNode
{
public:
	CSListNode* pLastNode;

	INLINE CSList() {pFirstNode = this; pLastNode = this;}

	INLINE void Append (CSListNode* pNode)
	{
		pNode->pNextNode = this;
		pLastNode->pNextNode = pNode;
		pLastNode = pNode;
	}
	INLINE void Prepend (CSListNode* pNode)
	{
		pNode->pNextNode = pFirstNode;
		pFirstNode = pNode;
		if (pLastNode==this) pLastNode = pNode;
	}
	INLINE bool IsNullNode(CSListNode* pNode) {return pNode==this;}

	INLINE CSListNode* GetFirstNode() {return pFirstNode;}
	INLINE CSListNode* GetLastNode() {return pLastNode;}

	INLINE void Reset() {pFirstNode = this; pLastNode = this;}
	INLINE void Invalidate()
	{
#ifndef NDEBUG
		pFirstNode = this;
		pLastNode = this;
#endif
	}
};


/*
 * Type-based single linked list template class
 */

template <typename TSListNode> class CSListOf: public CSList
{
public:
	INLINE TSListNode* GetFirstNode() {return (TSListNode*)CSList::GetFirstNode();}
	INLINE TSListNode* GetLastNode() {return (TSListNode*)CSList::GetLastNode();}
	INLINE TSListNode* GetNextNode(CSListNode* pNode) {return (TSListNode*)pNode->GetNextNode();}

	INLINE void Clear()
	{
		TSListNode* pCur = GetFirstNode();
		while (!IsNullNode(pCur))
		{
			TSListNode* pNext = GetNextNode(pCur);
			delete pCur;
			pCur = pNext;
		}
		Reset();
	}
};


#endif //__mylist_H__
/*
 * File:	TestNode.h
*/

#ifndef __TestNode_H__
#define __TestNode_H__

#include "mylist.h"


class CTestNode: public CSListNode
{
	int nVal;

public:
	CTestNode(int nInitVal);
	~CTestNode();

	virtual void PrintValue();
};


#endif //__TestNode_H__
/*
 * File:	TestNode.cpp
*/

#include <stdio.h>
#include "TestNode.h"


CTestNode::CTestNode(int nInitVal)
{
	printf("{%d}",nInitVal);
	nVal = nInitVal;
}

CTestNode::~CTestNode()
{
	printf("{~%d}",nVal);
}

void CTestNode::PrintValue()
{
	printf("[%d]",nVal);
}
/*
 * File:	main.cpp
*/

#include <stdio.h>
//#include <stdlib.h>
#include "mylist.h"
#include "TestNode.h"

void show_list();


CSListOf<CTestNode> g_TestList;


int main(int argc, char* argv[])
{
	show_list();

	g_TestList.Append(new CTestNode(100));
	g_TestList.Append(new CTestNode(200));
	g_TestList.Append(new CTestNode(300));
	g_TestList.Append(new CTestNode(400));
	g_TestList.Prepend(new CTestNode(-100));
	g_TestList.Prepend(new CTestNode(-200));
	g_TestList.Prepend(new CTestNode(-300));
	g_TestList.Prepend(new CTestNode(-400));
	puts("");
	show_list();

	g_TestList.Clear();
	puts("");
	show_list();

	return 0;
}

void show_list()
{
	puts("=======================================");
	for (CTestNode* pCur = g_TestList.GetFirstNode(); !g_TestList.IsNullNode(pCur); pCur = g_TestList.GetNextNode(pCur))
		pCur->PrintValue();
	puts("");
	puts("=======================================\n");
}

So, when I've disassembled my binary and looked at the function show_list()...

void show_list()
{
	puts("=======================================");
	for (CTestNode* pCur = g_TestList.GetFirstNode(); !g_TestList.IsNullNode(pCur); pCur = g_TestList.GetNextNode(pCur))
		pCur->PrintValue();
	puts("");
	puts("=======================================\n");
}

 

...I have seen THAT:

 

BadCode-1.pngBadCode-2.png

 

So, my question is: what is this strange unwanted code and how I can get rid of it?

Thanks, sorry for my curved English.

5 Replies
Copilot Lvl 2
Message 2 of 6

Re: What's that horrible pointer check in the gcc's generated code?

OK, I've guessed that additional code is the result of the pointer type casting from child class with virtual methods table (CTestNode*) to the base class without vtable (CSListNode*). In that manner the compiler checks the case of null pointer to the base class (and force application to crash if it is null).
When I remove 'virtual' keyword from the declaration of CTestNode the resulting machine code becomes pretty clean. But appiarence of the virtual methods destroys the idea of the good code.
Any idea to solve this dileme?

Commander Lvl 2
Message 3 of 6

Re: What's that horrible pointer check in the gcc's generated code?

You don't mention at what optimization level you are building with.  You might try the

command line option -fdelete-null-pointer-checks .

 

See here for some background.

 

Please follow-up to let us know how you made out. For good karma, mark a reply as the answer if it helped!

Copilot Lvl 2
Message 4 of 6

Re: What's that horrible pointer check in the gcc's generated code?

I already tryed -fdelete-null-pointer-checks. It didn't help.

Copilot Lvl 2
Message 5 of 6

Re: What's that horrible pointer check in the gcc's generated code?

I build this app in the such way:

C:\MinGW\bin\g++ -c -fno-rtti -D NDEBUG -O2 -march=native *.cpp
C:\MinGW\bin\g++ -static-libstdc++ -o quest_badptrcheck *.o

But it doesn't matter what the optimization level I applay. -O2, -O3 - never mind, these null-checks are still present.

Copilot Lvl 2
Message 6 of 6

Re: What's that horrible pointer check in the gcc's generated code?

After some experiments I did two following things.
First, I modified the declaration of CListOf<> class at following manner:

/*
 * Type-based single linked list template class
 */

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"

template <typename TSListNode> class CSListOf: public CSList  //modified!
{
public:
	INLINE TSListNode* GetFirstNode() {return (TSListNode*)((char*)CSList::GetFirstNode()-
										(offsetof(TSListNode,pNextNode)-offsetof(CSListNode,pNextNode)));}
	INLINE TSListNode* GetLastNode() {return (TSListNode*)((char*)CSList::GetLastNode()-
	                                                    (offsetof(TSListNode,pNextNode)-offsetof(CSListNode,pNextNode)));}
	INLINE TSListNode* GetNextNode(CSListNode* pNode) {return (TSListNode*)((char*)pNode->GetNextNode()-
														(offsetof(TSListNode,pNextNode)-offsetof(CSListNode,pNextNode)));}

	INLINE void Clear()
	{
		TSListNode* pCur = GetFirstNode();
		while (!IsNullNode(pCur))
		{
			TSListNode* pNext = GetNextNode(pCur);
			delete pCur;
			pCur = pNext;
		}
		Reset();
	}
};

#pragma GCC diagnostic pop

Yes, I understand this trick is not mutch perfect and is very compiller specific. However it works and caused appearance the single comparision with null instead it's double version (for child class and then for it's base one in the case of normal type cast).

 

Second, I found the GCC's option -fno-isolate-erroneous-paths-dereference and compile my app with it:

C:\MinGW\bin\g++ -c -fno-rtti -fno-isolate-erroneous-paths-dereference -D NDEBUG -O2 -march=native *.cpp
C:\MinGW\bin\g++ -static-libstdc++ -o quest_badptrcheck *.o

The result was removing the extra code aimed to stop 'undefined behavior' propagation by generating UD2 instruction (if I guessed all right).

The resulting code became much cleaner:

BadCode-3.png

 

However This parasite null-check was not gone! And now we have something like this:

if (I have a car) I drive this car;
else I drive this car.

If I have my chair I'm sitting my chair else I'm sitting my chair... Perfect logic! Obviosly the SUCH construction MUST be optimized! But it didn't. Inconceivably!
Maybe it is the GCC's optimizer bug? I would be very glad to ask GCC's developers about this issue!
Or maybe I understood something wrong? I hope anybody explain me...
Thank You for answers.