variable placement issue

variable placement issue

Robert Adair's picture

I have a problem where two different variables end up getting stored at the same memory location by the Intel compiler for Windows,IA32, v11.1.067. The code below runs finewith both theVS2010 and VS2005 compilers. Any workaround for the Intel compiler? I'm running the compiler in theVS2005 workspace.

// Predeclare the derived class so that it can be used for type casting
class CDerived;

// Define the base class
class CBase
{
public:
	int	nRows;

	CBase(int n=0) : nRows(n)
	{
	}

	// Cast this class to the derived class to allow the reference operator
	// to work with the Proxy function. For example:
	// CDerived		A;
	// CDerived&	B	= A.Proxy(4);
	operator CDerived&()
	{
		return reinterpret_cast(*this);
	}

	// Create a function that does nothing but return
	// an initialized object of this class type.
	CBase Proxy(int n)
	{
		return CBase(n);
	}
};

// Define the derived class
class CDerived : public CBase
{
public:
	CDerived(int n0=0) : CBase(n0)
	{
	}

	// Create a function that does nothing but return
	// an initialized object of this class type.
	CDerived ProxyA(int n)
	{
		return CDerived(n);
	}
};

int main()
{
	// Create a reference to the object returned by the ProxyA function
	// This all works fine
	CDerived	M(4);
	CDerived&	M0_ref	= M.ProxyA(3);
	CDerived&	M1_ref	= M.ProxyA(5);

	// Create a reference to the object returned by the Proxy function
	CDerived	N(4);
	CDerived&	N0_ref	= N.Proxy(3);

	// So far, everything is fine.  The next line causes a problem.
	// N1_ref ends up being placed at the memory location of N0_ref, so it
	// ends up overwriting N0_ref.
	CDerived&	N1_ref	= N.Proxy(5);

	return 0;
}
14 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
quocanle (Intel)'s picture

What is the nature of the failure? I had 11.1.065 on my system, and I don't see any problem.What is the command line option?

quocanle (Intel)'s picture

I just tried it with 11.1.067, and don't see any issues there either.

test>icl test.cpp

Intel C++ Compiler Professional for applications running on IA-32, Version 11.1 Build 20100806 Package ID: w_cproc

_p_11.1.067

Copyright (C) 1985-2010 Intel Corporation. All rights reserved.

test.cpp

Microsoft Incremental Linker Version 9.00.30729.01

Copyright (C) Microsoft Corporation. All rights reserved.

-out:test.exe

test.obj

test>test

test>cl test.cpp

Microsoft 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86

Copyright (C) Microsoft Corporation. All rights reserved.

test.cpp

Microsoft Incremental Linker Version 9.00.30729.01

Copyright (C) Microsoft Corporation. All rights reserved.

/out:test.exe

test.obj

test>test

test>

Robert Adair's picture

The compiler command line is:
/c /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /EHsc /RTC1 /MDd /GS /fp:fast /Fo"Debug/" /W3 /nologo /Wp64 /ZI

and for the linker:
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /OUT:"D:\Visual C++\trash\Intel Compiler Bug\Debug\Intel Compiler Bug.exe" /INCREMENTAL /nologo /MANIFEST /MANIFESTFILE:"Debug\Intel Compiler Bug.exe.intermediate.manifest" /TLBID:1 /DEBUG /PDB:"D:\Visual C++\trash\Intel Compiler Bug\Debug\Intel Compiler Bug.pdb" /SUBSYSTEM:CONSOLE /IMPLIB:"D:\Visual C++\trash\Intel Compiler Bug\Debug\Intel Compiler Bug.lib" /MACHINE:X86

What I'm seeing is that the assignment to N1_ref overwrites the assignment to N0_ref. When I look at &N1_ref and &N0_ref in the debugger, they're both the same value. I get the same behavior in both debug and release builds. Any ideas on other things to check?

quocanle (Intel)'s picture

This looks like aproblem in the compiler. I will investigate this issue and provide an update once I have a resolution.

jimdempseyatthecove's picture

Are you expecting

CDerived&N0_ref=N.Proxy(3);

to allocate an object instead of re-initializing N?

CDerivedN(4); // create CDerived object N,initialized to 4
CDerived&N0_ref=N.Proxy(3); // make reference to N, reinitialize to 3
CDerived&N1_ref=N.Proxy(5);// make reference to N, reinitialize to 5

Afteryou step through the ctor of N(4) don't you see N.nRows transition from 4, 3, 5?

Jim

www.quickthreadprogramming.com
quocanle (Intel)'s picture

Base on the test case above, this is what I see:

CDerived N(4);
CDerived& N0_ref = N.Proxy(3);
CDerived& N1_ref = N.Proxy(5);

std::cout << "N.nRows= " << N.nRows << std::endl << "N0_ref.nRows = "<< N0_ref.nRows << std::endl << "N1_ref.nRows = "<< N1_ref.nRows << std::endl;

Looks like ICL is re-initializing N, but MSVC is allocating an object instead of re-initializing.

MSVC
====
N.nRows= 4
N0_ref.nRows = 3
N1_ref.nRows = 5

ICL
====
N.nRows= 4
N0_ref.nRows = 5
N1_ref.nRows = 5

Robert Adair's picture

yes, this is the behavior I'm seeing from ICL

N.nRows= 4
N0_ref.nRows = 5
N1_ref.nRows = 5

The thing that has me bothered about ICL "reinitializing" the object is that when I look at &N0_ref and &N1_ref, they're the same using ICL but not MSVC. Shouldn't the addressesalways be different as they're diffent named variables?

Robert Adair's picture

Ok, I think I understand about why reinitializing putsN0_ref and N1_ref at the same memory location. Both ICL and MSVC perform the same in the following case:

int a;
int& a0_ref = a;
int& a1_ref = a;

The compilers apparently treat a0_ref and a1_ref as though they were in an anonymous union. I didn't realize that. I guess then I really need to understand which compiler is exhibiting the more accepted behavior for this case.

jimdempseyatthecove's picture

The compiler that is generating the different addresses is (arguably) in error.

Try:

CDerived N(4); 
std::cout << "CDerived N(4); " << N.nRows; << std::endl
CDerived& N0_ref = N.Proxy(3); 
std::cout << "CDerived& N0_ref = N.Proxy(3); " << N.nRows; << std::endl
CDerived& N1_ref = N.Proxy(5); 
std::cout << "CDerived& N1_ref = N.Proxy(5); " << N.nRows; << std::endl

Your Proxy/ProxyA return (ctor of object), as written,will (may)create an on-stack temporary object that immidately evaporates. Depending on "implementation detail" how stack temps are created will depend on whether this appears to be an anonomous union or seperated structs.

Correct me if I am wrong, I assume that you intend to reissue the ctor on CBase of the object N (of type CDerived) as opposed to creating seperate objects.

You cannot have multiple references to the same object of which that same object contains different states.

If you want seperate states then instantiate seperate objects.

If you want state changes to the same object where state change is effected upon creation of the reference, then the syntax gets a bit obscured (untested code)

    CDerived ProxyA(int n)   
    {   
        return *(
		new(static_cast(this)) CBase(*this)(n)
	);   
    }   

Jim Dempsey

www.quickthreadprogramming.com
Robert Adair's picture

Jim,

The short answer to why I care is that I havea problem that I think can be bettersolved by byrvalue references than my current implementation, but the intel compiler doesn't currently support them. In trying to simulate certain aspects of the design using ICL, I ran into this issue which looked like a bug to me, and since bugs may appear in other more standard cases, I thought it should be reported. In addition, I've learned a bit more about how compilers work.

Thanks,
Robert

quocanle (Intel)'s picture

rvalue references is now supported with the compiler that comes with Intel Parallel Composer 2011 suite. You need to enable the option below.

icl -Qstd=c++0x test.cpp

jimdempseyatthecove's picture

Robert,

Correct me if I am wrong, I very well could be as I am not up on the intricacies of this issue. Aren't R-value references valid only through the end of the current statement? IOW to facilitate use for args in function call and for source object for copy constructors?

My interpretaton of what you are trying to do is to make an R-value (newly constructed)reference persistant past the end of a statement.

Dmitriy, would you have a comment on this?

Jim Dempsey

www.quickthreadprogramming.com
quocanle (Intel)'s picture

We havefinished investigating this issue, and we have determinedthat thisis not a compiler problem, and the user should make the appropriate changes in the code.

The problematiccodeare listed here:

CDerived N(4);

CDerived& N0_ref = N.Proxy(3);

CDerived& N1_ref = N.Proxy(5);

std::cout << N0_ref.nRows;

The problem with this code is that it is accessing a temporary that is no longer valid. A temporary is created for the return value of N.Proxy(3) but its lifetime is that of the full expression. If this class had a destructor you would see that the destructor for this temporary would occur before the N.Proxy(5) call. Obviously you do not want to access an object that is already destructed.

So,Intel compiler, to reduce the size of the stack, reuses that temporary the next time it is needed. Microsoft instead appears to not reuse the temporary so it remains on the stack. Microsoft still does call the destructor at the end of the full expression though so this is obviously very dangerous and non-portable code.

Login to leave a comment.