This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
c++/6831: Wrong base class alignment (nvalign vs. align)
- From: Grigory_Zagorodnev at vniief dot ims dot intel dot com
- To: gcc-gnats at gcc dot gnu dot org
- Date: 27 May 2002 10:33:52 -0000
- Subject: c++/6831: Wrong base class alignment (nvalign vs. align)
- Reply-to: Grigory_Zagorodnev at vniief dot ims dot intel dot com
>Number: 6831
>Category: c++
>Synopsis: Wrong base class alignment (nvalign vs. align)
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: unassigned
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon May 27 03:36:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator: Grigory Zagorodnev
>Release: 3.1
>Organization:
>Environment:
host: ia64-unknown-linux
build: ia64-unknown-linux
target: ia64-unknown-linux
configured with: ./configure
>Description:
G++3 gives wrong layout of class E object in the testcase below.
1. The testcase
---------------
struct A {
int a;
};
struct B : public virtual A {};
struct C {
long double c;
};
struct D : public virtual C {
int d;
};
struct E : public B, public D {
int e;
};
2. The issue
------------
Expected layout of class E on ia64 is the following:
Offset Size Contents
[0000] 8 B's virtual table pointer (B is the primary base for E)
[0008] 8 D's virtual table pointer
[0010] 4 D::d
[0014] 4 Padding to round up D size to multiple of pointer align (8)
[0018] 4 E::e
[001c] 4 A::a
[0020] 16 C::c
But G++3 compiler gives another object layout for E:
Offset Size Contents
[0000] 8 B's virtual table pointer (B is the primary base for E)
[0008] 8 Padding
[0010] 8 D's virtual table pointer
[0018] 4 D::d
[001c] 4 E::e
[0020] 4 A::a
[0024] 12 Padding
[0030] 16 C::c
The layout is different and is wrong.
3. Details and analisis
-----------------------
We see difference in three points here, all related to padding bytes added.
I. No padding after D::d
Class D is laying out using common rules. It means that D should be finalized either - rounded up to a non-zero multiple of align(D). g++3 does not perform this step for base classes.
II. Extra padding after A::a
There is nothing wrong - C::c should be 16-byte aligned.
Difference is just a side-effect of inconsistency listed above.
III. Padding after B's virtual table pointer
This is the major inconsistency. Let's see...
The C++ ABI says [Chapter 2: Data Layout/2.4 Non-POD Class Types/II. Allocation of Members Other Than Virtual Bases]:
"if D is not an empty base class (including all data members), start at offset dsize(C), incremented if necessary for alignment to nvalign(type(D)) for base
classes or to align(type(D)) for data members. "
In our case, nvalign(D) == 8, align(D) == 16.
So, it looks like g++3 did increment address to keep 16-bytes alignment of class D. But this is valid for data members only and not for base classes. Such behaviour is wrong. Since D is the base class, it should be aligned to nvalign (D)==8 bytes within class E and there should not be any padding.
In other words, g++3 erroneously uses align(D) instead of nvalign(D).
>How-To-Repeat:
It's not so easy to dump object as showed above. So we are using run-time test which checks offset of class D within the object E. It used to be sizeof(void *), i.e. D is comming right after class B, when B contains only the virtual table pointer.
1. Build attached fail.cpp test using simple command line
g++ fail.cpp
2. Run it
./a.out
3. Look for 'passed' word in the output
Actual Results:
failed
D's offset is 16
expected 8
Expected Results: passed
>Fix:
>Release-Note:
>Audit-Trail:
>Unformatted:
----gnatsweb-attachment----
Content-Type: text/plain; name="fail.cpp"
Content-Disposition: inline; filename="fail.cpp"
#include <stdio.h>
struct A {
int a;
};
struct B : public virtual A {};
struct C {
long double c;
};
struct D : public virtual C {
int d;
};
struct E : public B, public D {
int e;
};
E e;
/* Expected layout of class E on ia64 is the following:
Offset Size Contents
[0000] 8 B's virtual table pointer (B is the primary base for E)
[0008] 8 D's virtual table pointer
[0010] 4 D::d
[0014] 4 Padding to round up D size to multiple of pointer align (8)
[0018] 4 E::e
[001c] 4 A::a
[0020] 16 C::c
*/
int main ()
{
// Offset of base class D is expected to be sizeof(void *)
size_t d_offset = ((char*) (D*) (&e)) - (char*) &e;
if( d_offset != sizeof(void *) ){
puts("failed");
printf("D's offset is %d\nexpected %d\n", d_offset, sizeof(void *));
} else
puts("passed");
}