Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intel C++ Problem with Polymorphic Registration #606

Open
nwhitehorn opened this issue Dec 13, 2019 · 4 comments
Open

Intel C++ Problem with Polymorphic Registration #606

nwhitehorn opened this issue Dec 13, 2019 · 4 comments

Comments

@nwhitehorn
Copy link

nwhitehorn commented Dec 13, 2019

We have been having some trouble with polymorphic types using ICC 19.0.3.199 20190206 (as installed at NERSC on Cori).

Trying to serialize a derived class by pointer-to-base results in an exception about an unregistered polymorphic type.

A very small test case can be found here:

https://github.com/mhasself/cereal_testing (ultra-minimal branch)

The error is "Trying to save an unregistered polymorphic type (MyClass1)", where MyClass1 is a derived type serialized by pointer to MyClass0, its parent. If serialized through a pointer to type MyClass1, everything works fine.

@avi369
Copy link

avi369 commented Jan 27, 2020

I have the same problem. Serialization via base pointer(std::unique_ptr in my case) appears broken. (same code work for GNU and CLANG). I appears that CEREAL_REGISTER_TYPE does not register the type. In my case the registration is done in the .cpp file.

@chaeyeunpark
Copy link

I got the same problem and looked a bit closer, and found that it is because of a bug (probably) of the Intel C++ compiler.

Loger version

The problem is that the constructor of InputBindingCreator/OutputBindingCreator is not called during the initialization of global variables.

Cereal uses those classes as a singleton (using StaticObject) and it expects they are initialized when the macro CEREAL_BIND_TO_ARCHIVE is used. The way Cereal uses it is rather implicit. The macro CEREAL_REGISTER_ARCHIVE defines a function the return type of which is polymorphic_serialization_support<Archive,T>::type, that makes CEREAL_BIND_TO_ARCHIVE instantiate polymorphic_serialization_support<Archive,T> for a user type T. As polymorphic_serialization_support<Archive,T> defines the method instantiate(), the instantiation process also expects this function is instantiated albeit not be called elsewhere. The Intel compiler also instantiates this function as expected (by using nm command on the final executable, we could find the name of such functions) thus no problem so far.

It turned out the problem is inside of the instantiate(). Inside the method, it uses create_bindings<Archive, T> which also should be instantiated. In create_bindings<Archive, T> class, there are two methods save/load that obtains a reference of a singleton InputBindingCreator<Archive, T>. These methods are also never used but the instantiation is sufficient to call the creator of this class which actually registers archive. This is where the bug appears. The intel compiler does not call such a constructor.

This bug is easily reproducible using the following code. It does not print anything when icc is used but gcc prints "Test construct" as expected. I also think this instantiation rule is what the standard guarantees (looks like p. 347 of n3337 says so).

#include <iostream>

template<typename T>
struct StaticObject
{
	static T& get()
	{
		static T t;
		(void)instance;
		return t;
	}

	static T& instance;
};
template<typename T>
T& StaticObject<T>::instance = StaticObject<T>::get();

struct Test
{
	Test()
	{
		std::cout << "Test construct" << std::endl;
	}

	void doNothing()
	{
	}
};

template<typename T>
struct Instantiator
{
	static void mustBeInstantiated() __attribute__((used))
	{
		StaticObject<T>::get().doNothing();
	}

	static void doSomething()
	{
	}
};
int main()
{
	Instantiator<Test>::doSomething();
	return 0;
}

I will report this bug to the intel compiler team but ain't sure when they will fix it.

@mhasself
Copy link

mhasself commented Apr 20, 2020

Thanks for looking into this more deeply, @chaeyeunpark.

But I wonder if the behaviour of your example is actually consistent with paragraph (6) on p. 347 of the linked standard: "If the overload resolution process can determine the correct function to call without instantiating a classtemplate definition, it is unspecified whether that instantiation actually takes place."

What happens if you remove the __attribute((used))__ from your example? That attribute might be enough to convince gcc that an instance of StaticObject<Test> must be created... but Intel docs do not list the "used" attribute, and perhaps icc just ignores it. https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-attributes

So does the difference rely on behaviour of a gcc extension?

Is there some other way to force-create the necessary instance, for the cereal application, that does not rely on attribute(used)?

To be clear, I have not tried to understand the cereal implementation we're talking about, in detail -- I'm just looking at your minimal example. Thanks again.

@chaeyeunpark
Copy link

A recent Intel OneAPI compiler with clang backend solves the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants