[boost.intrusive] Assertion failure with SunCC

The following simple program crashes with assertion failure when compiled under Solaris 11 with SunCC: Sun C++ 5.8 Patch 121018-10 2007/02/21 Assertion failed: node_algorithms::unique(to_insert), file boost/boost/intrusive/list.hpp, line 168 I'm using the latest Boost.Intrusive from CVS (snapshot taken 2007-06-03). I have tracked the problem, but I don't know wheter it's the code or compiler problem; see below the code for cause. == Begin code // Program to demonstrate a bug with SunCC and member hooks. #include <boost/intrusive/list.hpp> struct klass { void *data_klass; boost::intrusive::list_member_hook<> hook_; typedef boost::intrusive::list< boost::intrusive::list_member_hook<>::value_traits< klass, &klass::hook_>, false> list; }; static klass Gd; static klass::list Gl; int main(void) { => Gl.push_back(Gd); return 0; } == End code When the program is started, the hook's prev_ and next_ members are initialized correctly to &Gd.hook_ (0x8062dd8). The member_value_traits instance is by dbx resolved to boost::intrusive::detail::member_value_traits< klass,boost::intrusive::list_member_hook< (boost::intrusive::linking_policy)1,void*>, klass::hook_>::to_node_ptr ^^^^^^^^^^^^ Corresponds to the template P parameter. The statement MemberHookType* result = &(value.*P); sets result to 0x8062dd4 (4 bytes off from the hooks address). Finally, in push_back method, the to_insert value receives 0x8062dd4 which equals &Gd (ie. it points to the start of the object) instead of &Gd.hook_. Should this be reported as a compiler bug? BTW, I'll try using base hooks as a workaround. (PS: the code works fine with gcc4.2)

Hi Zeljko, Zeljko Vrba wrote:
The following simple program crashes with assertion failure when compiled under Solaris 11 with SunCC: Sun C++ 5.8 Patch 121018-10 2007/02/21
Assertion failed: node_algorithms::unique(to_insert), file boost/boost/intrusive/list.hpp, line 168
I'm using the latest Boost.Intrusive from CVS (snapshot taken 2007-06-03). I have tracked the problem, but I don't know wheter it's the code or compiler problem; see below the code for cause.
Now I must confess it: Boost.Intrusive uses a non-standard and non-portable hack to make member hooks as small as base hooks. This hack uses the fact that for a member stored in a class or in a superclass without any virtual inheritance relationship a pointer to member is just the offset of that member in the class. This offset is encoded differently for different compilers. If you see the file boost/intrusive/detail/parent_from_member.hpp you have the following function: template<class Parent, class Member> std::size_t offset_from_pointer_to_member(const Member Parent::* ptr_to_member) { //The implementation of a pointer to member is compiler dependent. #if defined(BOOST_MSVC) || defined(__GNUC__) || \ defined(BOOST_INTEL) || defined(__HP_aCC) || \ defined(__EDG_VERSION__) //This works with gcc, msvc, edg, ac++ return *(const std::size_t*)(const void*)&ptr_to_member; #else //This is the traditional C-front approach: CW 9.4, dmc return *(const std::size_t*)(const void*)&ptr_to_member - 1; #endif } This function takes a pointer to member and tries to guess the offset between the class that owns the member and the member. As you can see, for Visual, Intel and GCC a pointer to data member it's just the offset. For compilers like DMC and CW is the offset minus 1. Since I don't have access to a SunCC compiler you will need to help me discover how SunCC implements pointer to data members. I will need for example the size of a pointer to data member (a simple sizeof(ptr_to_member) would be enough), and what the binary value of of that pointer to member for different layouts, say: struct klass { void *data_klass[N]; boost::intrusive::list_member_hook<> hook_; typedef boost::intrusive::list< boost::intrusive::list_member_hook<>::value_traits< klass, &klass::hook_>, false> list; }; changing the value of N. Willing to help? Regards, Ion P.S.: I'm not sure if EDG-based compilers and HP compiler work because I don't have such compilers. If someone wants to help on those, I would be glad.

On Mon, Jun 04, 2007 at 05:10:31PM +0200, Ion Gaztañaga wrote:
Since I don't have access to a SunCC compiler you will need to help me discover how SunCC implements pointer to data members. I will need for
It's downloadable for free, both for Linux and Solaris :)
example the size of a pointer to data member (a simple sizeof(ptr_to_member) would be enough), and what the binary value of of that pointer to member for different layouts, say:
The program at the bottom prints the following for NN=10 and NN=20: 4 45 4 85 Do you need more information? == #include <iostream> #include <boost/intrusive/list.hpp> struct klass { void *data_[NN]; char ch_; boost::intrusive::list_member_hook<> hook_; typedef boost::intrusive::list< boost::intrusive::list_member_hook<>::value_traits< klass, &klass::hook_>, false> list; }; int main(void) { using std::cout; using std::endl; boost::intrusive::list_member_hook<> klass::*ptr = &klass::hook_; cout << sizeof(ptr) << ' ' << *(size_t*)&ptr << endl; return 0; } ==

Zeljko Vrba wrote:
On Mon, Jun 04, 2007 at 05:10:31PM +0200, Ion Gaztañaga wrote:
Since I don't have access to a SunCC compiler you will need to help me discover how SunCC implements pointer to data members. I will need for
It's downloadable for free, both for Linux and Solaris :)
I've just started downloading Solaris Express for Developers, but it's taking a while ;-)
The program at the bottom prints the following for NN=10 and NN=20:
4 45 4 85
I'm a bit puzzled, because it seems odd that the hook is not aligned to 4 bytes even if you have a char between data and the hook. The compiler might be reordering things a bit. Could you check the real distance between klass and hook_? Something like: int main(void) { using std::cout; using std::endl; klass k; cout << ((char*)k.hook_ - (char*)k)sizeof(ptr) << endl; return 0; } If the distance is exactly 45 and 85 then you can try to add sunCC detection in the first part of the parent from member function and see if everything goes well. If the real distance is something different, we'll need to try something new. Thanks for the help, Ion

On Mon, Jun 04, 2007 at 08:54:03PM +0200, Ion Gaztañaga wrote:
I'm a bit puzzled, because it seems odd that the hook is not aligned to 4 bytes even if you have a char between data and the hook. The compiler might be reordering things a bit.
No, the data stored in the pointer-to-member is *not* the "real" offset, contrary to your assumption :) When checking in the debugger, the hook is aligned properly.
Could you check the real distance between klass and hook_? Something like:
The statement (after adding 'klass k' in the local scope of main()) cout << (char*)&k.hook_ - (char*)&k << endl; prints out 84. Furthermore, given the following program: == #include <boost/intrusive/list.hpp> struct klass { void *data_klass[10]; boost::intrusive::list_member_hook<> hook_; typedef boost::intrusive::list< boost::intrusive::list_member_hook<>::value_traits< klass, &klass::hook_>, false> list; }; static klass Gd; static klass::list Gl; int main(void) { Gl.push_back(Gd); return 0; } == I step the execution to list::push_back and its first statement node_ptr to_insert = ValueTraits::to_node_ptr(value); The debugger reports: (dbx) print -r *to_insert *to_insert = { node::prev_ = (nil) node::next_ = (nil) } (dbx) p to_insert to_insert = 0x8062e2c (dbx) up Current function is main 19 Gl.push_back(Gd); (dbx) p &Gd &Gd = 0x8062e2c So, the "to_insert" does not point to the hook address, but to the very beginning of the structure. BTW, this is with SunStudio 12: CC: Sun C++ 5.9 SunOS_i386 2007/05/03

Zeljko Vrba wrote:
No, the data stored in the pointer-to-member is *not* the "real" offset, contrary to your assumption :) When checking in the debugger, the hook is aligned properly.
I just realized about this just after sending my previous mail. SunCC seems to use the classical cfront approach encoding a pointer to member as offset + 1. So it seems that parent_from_member detection is correct for SunCC.
Could you check the real distance between klass and hook_? Something like:
The statement (after adding 'klass k' in the local scope of main())
cout << (char*)&k.hook_ - (char*)&k << endl;
prints out 84.
Thanks. Really useful information.
Furthermore, given the following program: [snip]
int main(void) { Gl.push_back(Gd); return 0; } [snip] So, the "to_insert" does not point to the hook address, but to the very beginning of the structure.
So the problem lies in ValueTraits::to_node_ptr() which just should use the pointer to member template to get the address of the hook. But for some reason, it does not work. The real logic for the conversion is boost/intrusive/detail/utilities.hpp in the member_value_traits class: static node_ptr to_node_ptr(reference value) { MemberHookType* result = &(value.*P); return result->to_node_ptr(); } So I'm a bit puzzled this does not work (we are not using the non-standard hack here). If you can debug it further, let me know. Meanwhile, I will download the SunCC version for Linux to see what happens. Thanks again for your help, Ion

On Tue, Jun 05, 2007 at 04:35:16PM +0200, Ion Gaztañaga wrote:
I just realized about this just after sending my previous mail. SunCC seems to use the classical cfront approach encoding a pointer to member as offset + 1. So it seems that parent_from_member detection is correct for SunCC.
Slightly off-topic, but: do you know the background WHY did they decide to implement ptr-to-member as offset +1?
boost/intrusive/detail/utilities.hpp in the member_value_traits class:
static node_ptr to_node_ptr(reference value) { MemberHookType* result = &(value.*P); return result->to_node_ptr(); }
I have tracked the conversion to here myself. I wondered whether this was a compiler bug, but the following simple program works, ie. it does NOT raise an assertion failure. I hope I managed to reproduce correctly the usage pattern of ptr-to-member.. == #include <assert.h> struct A { int x; int y; }; template<typename T, int T::*P> struct setter { static void f(T &t) { t.*P = 42; } }; int main(void) { A a; a.x = a.y = 0; assert((a.x == 0) && (a.y == 0)); setter<A, &A::y>::f(a); assert((a.x == 0) && (a.y == 42)); setter<A, &A::x>::f(a); assert((a.x == 42) && (a.y == 42)); return 0; } == I'm kinda clueless as how to proceed from here. If it _is_ a genuine compiler bug, we need a more complex test-case, which I'm unable to produce myself (as I don't know how many intermediate instantiation steps there are in the intrusive library).

Zeljko Vrba wrote:
On Tue, Jun 05, 2007 at 04:35:16PM +0200, Ion Gaztañaga wrote:
I just realized about this just after sending my previous mail. SunCC seems to use the classical cfront approach encoding a pointer to member as offset + 1. So it seems that parent_from_member detection is correct for SunCC.
Slightly off-topic, but: do you know the background WHY did they decide to implement ptr-to-member as offset +1?
Because you need to represent a null pointer to data member. So they choose to represent a null pointer to member as 0 (which is a valid offset inside the object) and add 1 to the valid offset to represent a valid offset. The offset + 1 approach is shared by more implementations (CW for windows and Digital Mars, for example). This was the historical presentation C-front chose, so I think many compilers implement it like that because of this historical heritage. Other implementations used another encoding. For example, the Itanium C++ ABI says (http://www.codesourcery.com/cxx-abi/abi.html#member-pointers): "A pointer to data member is an offset from the base address of the class object containing it, represented as a ptrdiff_t. It has the size and alignment attributes of a ptrdiff_t. A NULL pointer is represented as -1."
I have tracked the conversion to here myself. I wondered whether this was a compiler bug, but the following simple program works, ie. it does NOT raise an assertion failure. I hope I managed to reproduce correctly the usage pattern of ptr-to-member..
[snip]
I'm kinda clueless as how to proceed from here. If it _is_ a genuine compiler bug, we need a more complex test-case, which I'm unable to produce myself (as I don't know how many intermediate instantiation steps there are in the intrusive library).
This seems a strange issue. I'll try to investigate it when I install the Sun compiler. Thanks for your effort! Ion

Ion Gaztanaga wrote:
P.S.: I'm not sure if EDG-based compilers and HP compiler work because I don't have such compilers. If someone wants to help on those, I would be glad.
Below are the results on HP-UX/ia64 in 32- and 64-bit pointer mode using the program posted by Zeljko Vrba. The compiler on HP-UX/ia64 is EDG-based so checking both __HP_aCC and __EDG_VERSION__ in boost/intrusive/detail/parent_from_member.hpp is reduntant. bash-2.03$ aCC -V aCC: HP C/aC++ B3910B A.06.14 [Feb 22 2007] bash-2.03$ aCC -I./boost -DNN=10 x.cpp && a.out 4 44 bash-2.03$ aCC -I./boost -DNN=20 x.cpp && a.out 4 84 bash-2.03$ aCC -I./boost -DNN=10 +DD64 x.cpp && a.out 8 88 bash-2.03$ aCC -I./boost -DNN=20 +DD64 x.cpp && a.out 8 168 bash-2.03$ x.cpp ----- #include <iostream> #include <boost/intrusive/list.hpp> struct klass { void *data_[NN]; char ch_; boost::intrusive::list_member_hook<> hook_; typedef boost::intrusive::list< boost::intrusive::list_member_hook<>::value_traits< klass, &klass::hook_>, false> list; }; int main(void) { using std::cout; using std::endl; boost::intrusive::list_member_hook<> klass::*ptr = &klass::hook_; cout << sizeof(ptr) << ' ' << *(size_t*)&ptr << endl; return 0; } ----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: "Boost User List" <boost-users@lists.boost.org> Sent: Monday, June 04, 2007 11:10 AM Subject: Re: [Boost-users] [boost.intrusive] Assertion failure with SunCC
Hi Zeljko,
Zeljko Vrba wrote:
The following simple program crashes with assertion failure when compiled under Solaris 11 with SunCC: Sun C++ 5.8 Patch 121018-10 2007/02/21
Assertion failed: node_algorithms::unique(to_insert), file boost/boost/intrusive/list.hpp, line 168
I'm using the latest Boost.Intrusive from CVS (snapshot taken 2007-06-03). I have tracked the problem, but I don't know wheter it's the code or compiler problem; see below the code for cause.
Now I must confess it: Boost.Intrusive uses a non-standard and non-portable hack to make member hooks as small as base hooks. This hack uses the fact that for a member stored in a class or in a superclass without any virtual inheritance relationship a pointer to member is just the offset of that member in the class. This offset is encoded differently for different compilers. If you see the file boost/intrusive/detail/parent_from_member.hpp you have the following function:
template<class Parent, class Member> std::size_t offset_from_pointer_to_member(const Member Parent::* ptr_to_member) { //The implementation of a pointer to member is compiler dependent. #if defined(BOOST_MSVC) || defined(__GNUC__) || \ defined(BOOST_INTEL) || defined(__HP_aCC) || \ defined(__EDG_VERSION__) //This works with gcc, msvc, edg, ac++ return *(const std::size_t*)(const void*)&ptr_to_member; #else //This is the traditional C-front approach: CW 9.4, dmc return *(const std::size_t*)(const void*)&ptr_to_member - 1; #endif }
This function takes a pointer to member and tries to guess the offset between the class that owns the member and the member. As you can see, for Visual, Intel and GCC a pointer to data member it's just the offset. For compilers like DMC and CW is the offset minus 1.
Since I don't have access to a SunCC compiler you will need to help me discover how SunCC implements pointer to data members. I will need for example the size of a pointer to data member (a simple sizeof(ptr_to_member) would be enough), and what the binary value of of that pointer to member for different layouts, say:
struct klass {
void *data_klass[N];
boost::intrusive::list_member_hook<> hook_;
typedef boost::intrusive::list<
boost::intrusive::list_member_hook<>::value_traits<
klass, &klass::hook_>,
false> list;
};
changing the value of N. Willing to help?
Regards,
Ion
P.S.: I'm not sure if EDG-based compilers and HP compiler work because I don't have such compilers. If someone wants to help on those, I would be glad. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Boris Gubenko wrote:
Ion Gaztanaga wrote:
P.S.: I'm not sure if EDG-based compilers and HP compiler work because I don't have such compilers. If someone wants to help on those, I would be glad.
Below are the results on HP-UX/ia64 in 32- and 64-bit pointer mode using the program posted by Zeljko Vrba.
The compiler on HP-UX/ia64 is EDG-based so checking both __HP_aCC and __EDG_VERSION__ in boost/intrusive/detail/parent_from_member.hpp is reduntant.
bash-2.03$ aCC -V aCC: HP C/aC++ B3910B A.06.14 [Feb 22 2007] bash-2.03$ aCC -I./boost -DNN=10 x.cpp && a.out 4 44 bash-2.03$ aCC -I./boost -DNN=20 x.cpp && a.out 4 84 bash-2.03$ aCC -I./boost -DNN=10 +DD64 x.cpp && a.out 8 88 bash-2.03$ aCC -I./boost -DNN=20 +DD64 x.cpp && a.out 8 168 bash-2.03$
So it seems that the compiler aligns the hook to 4 bytes: void *data_[NN]; //4*NN bytes char ch_; //1 byte plus alignment so the trick used to extract the offset in parent_from_member function is adequate. But the regression tests for Intrusive with HP aCC: http://engineering.meta-comm.com/boost-regression/CVS-HEAD/developer/intrusi... seems to fail in tests using member hooks. A bit strange. Can you check the real distance between members with something like: int main(void) { using std::cout; using std::endl; klass k; cout << ((char*)k.hook_ - (char*)k) << endl; return 0; } If the distance is equal to the raw values extracted from the pointer to data member, then regression test failures might be provoked by another library bug. Regards, Ion

Ion Gaztanaga wrote:
If the distance is equal to the raw values ...
It is equal: bash-2.03$ aCC -I./boost -DNN=10 x.cpp && a.out 4 44 44 bash-2.03$ aCC -I./boost -DNN=20 x.cpp && a.out 4 84 84 bash-2.03$ aCC -I./boost -DNN=10 +DD64 x.cpp && a.out 8 88 88 bash-2.03$ aCC -I./boost -DNN=20 +DD64 x.cpp && a.out 8 168 168 bash-2.03$ x.cpp ----- #include <iostream> #include <boost/intrusive/list.hpp> #include <cstddef> struct klass { void *data_[NN]; char ch_; boost::intrusive::list_member_hook<> hook_; }; int main(void) { using std::cout; using std::endl; klass k; ptrdiff_t distance = (char*)&k.hook_ - (char*)&k; boost::intrusive::list_member_hook<> klass::*ptr = &klass::hook_; cout << sizeof(ptr) << ' ' << offsetof(klass, hook_) << endl; cout << distance << endl; return 0; } ----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: "Boost User List" <boost-users@lists.boost.org> Sent: Monday, June 04, 2007 3:01 PM Subject: Re: [Boost-users] [boost.intrusive] Assertion failure with SunCC
Boris Gubenko wrote:
Ion Gaztanaga wrote:
P.S.: I'm not sure if EDG-based compilers and HP compiler work because I don't have such compilers. If someone wants to help on those, I would be glad.
Below are the results on HP-UX/ia64 in 32- and 64-bit pointer mode using the program posted by Zeljko Vrba.
The compiler on HP-UX/ia64 is EDG-based so checking both __HP_aCC and __EDG_VERSION__ in boost/intrusive/detail/parent_from_member.hpp is reduntant.
bash-2.03$ aCC -V aCC: HP C/aC++ B3910B A.06.14 [Feb 22 2007] bash-2.03$ aCC -I./boost -DNN=10 x.cpp && a.out 4 44 bash-2.03$ aCC -I./boost -DNN=20 x.cpp && a.out 4 84 bash-2.03$ aCC -I./boost -DNN=10 +DD64 x.cpp && a.out 8 88 bash-2.03$ aCC -I./boost -DNN=20 +DD64 x.cpp && a.out 8 168 bash-2.03$
So it seems that the compiler aligns the hook to 4 bytes:
void *data_[NN]; //4*NN bytes char ch_; //1 byte plus alignment
so the trick used to extract the offset in parent_from_member function is adequate. But the regression tests for Intrusive with HP aCC:
http://engineering.meta-comm.com/boost-regression/CVS-HEAD/developer/intrusi...
seems to fail in tests using member hooks. A bit strange. Can you check the real distance between members with something like:
int main(void) { using std::cout; using std::endl; klass k; cout << ((char*)k.hook_ - (char*)k) << endl; return 0; }
If the distance is equal to the raw values extracted from the pointer to data member, then regression test failures might be provoked by another library bug.
Regards,
Ion _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Boris Gubenko wrote:
Ion Gaztanaga wrote:
If the distance is equal to the raw values ...
It is equal:
Thanks for your info. So aCC pointer to member is really the offset. This means that parent_from_member function is correctly configured for aCC but for some other reason regression tests fail. Regards, Ion

Hi, On 6/4/07, Ion Gaztañaga <igaztanaga@gmail.com> wrote:
P.S.: I'm not sure if EDG-based compilers and HP compiler work because I don't have such compilers. If someone wants to help on those, I would be glad.
For HP development environment, you can create an account at: http://www.testdrive.hp.com/ They offer free accounts on a whole range of HP operating systems and architectures. with best regards, dhruva -- Dhruva Krishnamurthy Contents reflect my personal views only!

dhruva wrote:
For HP development environment, you can create an account at: http://www.testdrive.hp.com/ They offer free accounts on a whole range of HP operating systems and architectures.
Thanks! I tried the sourceforge compiler farm but they shut it down. Glad to know there is still hope. Regards, Ion
participants (5)
-
Boris Gubenko
-
dhruva
-
Ion Gaztañaga
-
Zeljko Vrba
-
Zeljko Vrba