This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

c++/6831: Wrong base class alignment (nvalign vs. align)



>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");
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]