This is an archive of the discontinued LLVM Phabricator instance.

[AIX] Implement AIX special alignment rule about double/long double
ClosedPublic

Authored by Xiangling_L on May 11 2020, 9:06 AM.

Details

Summary

Implement AIX default power alignment rule by adding PreferredAlignment and PreferredNVAlignment in ASTRecordLayout class.

The implementation aims at returning correct value for __alignof(x) and alignof(x) under power alignment rules.

The AIX power alignment rules apply the natural alignment of the
"first member" if it is of a floating-point data type (or is an aggregate
whose recursively "first" member or element is such a type). The alignment
associated with these types for subsequent members use an alignment value
where the floating-point data type is considered to have 4-byte alignment.

For the purposes of the foregoing: vtable pointers, non-empty base classes,
and zero-width bit-fields count as prior members; members of empty class
types marked no_unique_address are not considered to be prior members.

Notes:

  1. ASTRecordLayout Layout.Alignment records the min alignment/ABI align of object, which is also the value _Alignof(x) should return;

2.Layout.NonVirtualAlignment records min alignment of nv part of object

  1. The size/nvsize of object will be rounded up to PreferredAlignment/PreferredNVAlignment on AIX.

4.getPreferredTypeAlign = __alignof(x) = preferred alignment of object

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Xiangling_L added inline comments.Jun 4 2020, 4:50 PM
clang/lib/AST/ASTContext.cpp
2418

Thank you for your suggestion. I think currently, there are three targets disabling this query, XCore, X86 and MSP430. Since @efriedma mentioned we tried to avoid OS-specific checks here, I am not sure if mark this completely-target-specific is a good idea? Personally, I think a query like allowsLargerPreferedTypeAlignment() would give more semantic meaning.

Xiangling_L edited the summary of this revision. (Show Details)Jun 4 2020, 5:58 PM
efriedma added inline comments.Jun 4 2020, 10:47 PM
clang/lib/AST/RecordLayoutBuilder.cpp
661

Please explain here what the different values (-1, 0, 1, 2) mean. Or use an enum.

Replace int with an more self-explanatory enum;

efriedma added inline comments.Jun 5 2020, 2:02 PM
clang/lib/AST/RecordLayoutBuilder.cpp
667

Instead of specifically tracking whether you've found an OverlappingEmpty field, could you just have something like "bool FoundNonOverlappingEmptyField = false;", and set it to true when you handle a field that isn't OverlappingEmpty? I don't think we need to specifically track whether we've found an OverlappingEmpty field.

Xiangling_L marked 2 inline comments as done.

Simplify the code;
Add one more testcase related to [[no_unique_addr]];

clang/lib/AST/RecordLayoutBuilder.cpp
667

I think you are right. We do not need to specifically track whether we've found an OverlappingEmpty field. But I think we do need an enum to track if the first non-OverlappingEmptyField is handled or not.

Or the following case is problematic:

struct A {
  int : 0;
  double d;
};

The double d will mistakenly match FieldOffset == CharUnits::Zero() && D->getFieldIndex() != 0 && !IsOverlappingEmptyField && FoundNonOverlappingEmptyField , which we shouldn't because it is not the first member of the struct.

efriedma added inline comments.Jun 8 2020, 7:00 PM
clang/lib/AST/RecordLayoutBuilder.cpp
667

Okay, so now there are three states. But the FirstNonOverlappingEmptyFieldFound is transient: on exit from ItaniumRecordLayoutBuilder::LayoutField, FirstNonOverlappingEmptyFieldStatus never contains FirstNonOverlappingEmptyFieldFound. I think it would be more clear to use a local variable for that.

1922

I don't think you need to distinguish the D->getFieldIndex() == 0 case from the D->getFieldIndex() != 0 case.

Xiangling_L marked an inline comment as done.

Replace the transient status by a local var;
Clean up the code;

This revision is now accepted and ready to land.Jun 9 2020, 10:00 AM
jasonliu added inline comments.Jun 10 2020, 4:11 PM
clang/include/clang/AST/RecordLayout.h
75

nit for comment: I don't think it's related to performance nowadays, and it's more for ABI-compat reason.

clang/lib/AST/ASTContext.cpp
2410

static_cast instead of c cast?

clang/lib/AST/RecordLayoutBuilder.cpp
827

We added supportsAIXPowerAlignment, so let's make use of that.
We could replace every call to isAIXLayout with supportsAIXPowerAlignment. This name is more expressive as well.

1233

Use a lambda for this query would be more preferable if same logic get used twice.

1922

Maybe it's a naive thought, but is it possible to replace NonOverlappingEmptyFieldFound with IsOverlappingEmptyField && FieldOffsets.size() == 0?

1996

hmm... Are we sure we should use FieldAlign instead of PreferredAlign here?
Same for the else below.

clang/lib/Basic/Targets/PPC.h
389

nit: use "else if" with the above if statement?
If it enters one of the Triples above, it's not necessary to check if it is AIX any more.

420–421

This could get combined with the new if for AIX below.

431

nit: use "else if" with the above if statement?

clang/test/Layout/aix-no-unique-address-with-double.cpp
63

Not an action item, but just an interesting note that, in some cases(like this one) power alignment rule could reverse the benefit of having [[no_unique_address]] at the first place.

clang/include/clang/Basic/TargetInfo.h
1400

Minor nit: Use backquotes around the XL option value "power".

clang/lib/AST/ASTContext.cpp
2421–2423

Comment should be updated.

2432

Does supportsAIXPowerAlignment express the condition we want to check here? That might be true for an implementation operating with mac68k alignment rules.

clang/lib/AST/RecordLayoutBuilder.cpp
661

I suggest to replace (if correct) "non-overlappingEmptyField" with "non-empty or empty-but-non-overlapping field".

663

Having "handled" at the front is a better indication (aside from the type) that "field" is not the main term. Also, minor nit: there's trailing whitespace at the end of the line here.

clang/lib/AST/RecordLayoutBuilder.cpp
1928

I tried clang -cc1 -triple powerpc-ibm-aix -fsyntax-only with
<stdin>:

struct A {
  struct B {
    double d[3];
  } b;
};

extern char x[__alignof__(struct A)];
extern char x[8];

I got an assertion failure:

clang: /src/clang/lib/AST/RecordLayoutBuilder.cpp:2078: void {anonymous}::ItaniumRecordLayoutBuilder::UpdateAlignment(clang::CharUnits, clang::CharUnits, clang::CharUnits): Assertion `llvm::isPowerOf2_64(PreferredNewAlignment.getQuantity()) && "Alignment not a power of 2"' failed.
clang/test/Layout/aix-double-struct-member.cpp
2

There's no testing for _Complex types.

struct A {
  struct B {
    long double _Complex d[3];
  } b;
};

extern char x[__alignof__(struct A)];
extern char x[8];
Xiangling_L marked 24 inline comments as done.

Addressed comments;
Fixed the ICE problem with array as first member;
Add support for Complex type;

clang/lib/AST/ASTContext.cpp
2432

Yeah, supportsAIXPowerAlignment cannot separate the preferred alignment of double, long double between power/natural and mac68k alignment rules. But I noticed that currently, AIX target on wyvern or XL don't support mac68k , so maybe we should leave further changes to the patch which is gonna implement mac68k alignment rules? The possible solution I am thinking is we can add checking if the decl has AlignMac68kAttr into query to separate things out.

Another thing is that once we start supporting mac68k alignment rule(if we will), should we also change the ABI align values as well? (e.g. for double, it should be 2 instead)

clang/lib/AST/RecordLayoutBuilder.cpp
661

Thanks for your suggestion. But I kinda prefer using NonOverlappingEmptyField, it is more consistent with IsOverlappingEmptyField. And also the equivalent name to NonOverlappingEmptyField is NonOverlappingAndNonEmptyField which is tedious I think.

1922

I don't think these two work the same. NonOverlappingEmptyFieldFound represents the 1st non-empty and non-overlapping field in the record. IsOverlappingEmptyField && FieldOffsets.size() == 0 represents something opposite.

1996

The way I see the PreferredAlign is not the real alignment the record uses (which I believe the community don't expect us to see it that way either). All code here should be attached to ABI align. Specially, on AIX, PreferredAlign is something we use to round up the record size, but it won't affect the record layout besides of that. So in other words, we are just tracking what PreferredAlignment value is here.

clang/lib/AST/ASTContext.cpp
2432

If the "base state" is AIX power alignment for a platform, I suggest that the name be defaultsToAIXPowerAlignment.

clang/lib/AST/RecordLayoutBuilder.cpp
661

I am suggesting a change to the comment and not the name here. If both the comment and the name uses the same (possibly confusing) form to express the concept, then the comment does not aid comprehension of the code.

Xiangling_L marked 2 inline comments as done.

Adjust the function name;
Adjust the comment;

jasonliu added inline comments.Jun 23 2020, 7:09 PM
clang/lib/AST/RecordLayoutBuilder.cpp
1922

You are right. I meant could we replace it with !(IsOverlappingEmptyField && FieldOffsets.size() == 0)?

Xiangling_L marked 2 inline comments as done.Jun 24 2020, 6:33 AM
Xiangling_L added inline comments.
clang/lib/AST/RecordLayoutBuilder.cpp
1922

I don't think so. The replacement does not work for the case:

struct A {
  int : 0;
  double d;
};

where __alignof(A) should be 4;

jasonliu added inline comments.Jun 24 2020, 8:17 AM
clang/lib/AST/RecordLayoutBuilder.cpp
1922

Got it. Thanks!

jasonliu accepted this revision.Jun 25 2020, 12:19 PM

LGTM. I have no further comments on this patch.

clang/include/clang/AST/RecordLayout.h
75

I'm still evaluating this; however, I might as well note a minor nit first:
s/perserving/preserving/g;
for all instances within this patch.

Xiangling_L marked 2 inline comments as done.

Corrected the comments;

clang/test/Layout/aix-double-struct-member.cpp
2

I am concerned that none of the tests actually create an instance of the classes under test and check the alignment (or related adjustments) in the IR. That is, we set up the preferred alignment value but don't check that we use it where we should.

As it is, it seems array new/delete has problems:

#include <assert.h>
extern "C" void *calloc(decltype(sizeof 0), decltype(sizeof 0));
extern "C" void free(void *);
extern "C" int printf(const char *, ...);

extern void *allocated_ptr;
extern decltype(sizeof 0) allocated_size;
struct B {
  double d;
  ~B() {}
  static void *operator new[](decltype(sizeof 0) sz);
  static void operator delete[](void *p, decltype(sizeof 0) sz);
};
B *allocBp();

#ifdef ALLOCBP
void *allocated_ptr;
decltype(sizeof 0) allocated_size;
void *B::operator new[](decltype(sizeof 0) sz) {
  void *alloc = calloc(1u, allocated_size = sz);
  printf("%p: %s\n", alloc, __PRETTY_FUNCTION__);
  printf("%zu\n", sz);
  return allocated_ptr = alloc;
}
void B::operator delete[](void *p, decltype(sizeof 0) sz) {
  printf("%p: %s\n", p, __PRETTY_FUNCTION__);
  printf("%zu\n", sz);
  assert(sz == allocated_size);
  assert(p == allocated_ptr);
  free(p);
}
B *allocBp() { return new B[2]; }
#endif

#ifdef MAIN
int main(void) { delete[] allocBp(); }
#endif

The xlclang++ invocation from XL C/C++ generates padding before the 32-bit new[] cookie. I'm not seeing that padding with this patch.

clang/test/Layout/aix-double-struct-member.cpp
3

My build with this patch also hits cases where the preferred alignment is increased to 8, but the offset of the associated member is not 8-byte aligned:

extern "C" int printf(const char *, ...);
struct A0 {};
struct Y : A0 {
  double mem;
};
struct Z : A0 {
  Y y;
};
extern char alignz[__alignof__(Z)];
extern char alignz[8];
int main(void) {
  Z z;
  printf("%td\n",
         &reinterpret_cast<char &>(z.y) - &reinterpret_cast<char &>(z));
}
*** Dumping AST Record Layout
         0 | struct Z
         0 |   struct A0 (base) (empty)
         4 |   struct Y y
         4 |     struct A0 (base) (empty)
         4 |     double mem
           | [sizeof=16, dsize=12, align=4, preferredalign=8,
           |  nvsize=12, nvalign=4, preferrednvalign=8]
238

Typo: s/tes8/test8/;

348

Same comment.

clang/lib/AST/ASTContext.cpp
2421–2423

I suggest to clarify the binding of the parenthetical and also to make the context that the required alignment is lower than the natural alignment more explicit:

// Double (and, for targets supporting AIX `power` alignment, long double) and
// long long should be naturally aligned (despite requiring less alignment) if
// possible.
clang/lib/AST/RecordLayoutBuilder.cpp
1216–1217

This comment belongs inside the lambda.

1217

The lambda is currently being named for what is produced based on the identity of what gets passed to it and not named for what it does.

This should be named getPackedBaseAlignFromUnpacked or similar.

1219

I suggest not applying "ABI compat" with Clang <= 6 on AIX. There was is no Clang <= 6 with AIX support.

1269

Suggestion:

// AIX `power` alignment does not apply the preferred alignment for non-union
// classes if the source of the alignment (the current base in this context)
// follows introduction of the first member with allocated space.
1801

The name is wrong for the value (the value corresponding to this name is !IsOverlappingEmptyField). I would suggest something like FoundNonOverlappingEmptyFieldToHandle.

1804

Move the definition of the variable here and just use the if condition as the initializer.

Xiangling_L marked an inline comment as done.Jul 2 2020, 8:01 AM
Xiangling_L added inline comments.
clang/test/Layout/aix-double-struct-member.cpp
2

Thank. I will create more practical testcases as you mentioned in your concern. And regarding to padding before the 32-bit new[] cookie issue, I am wondering is that part of power alignment rule or what rules do we follow to generate this kind of padding?

clang/test/Layout/aix-double-struct-member.cpp
2

The padding has to do with the alignment. The allocation function returns 8-byte aligned memory. The 32-bit cookie takes 4 of the first 8 bytes. The type's preferred alignment is 8, so there are 4 bytes of padding.

clang/lib/AST/RecordLayoutBuilder.cpp
1269

Adjustment to my suggestion:
s/first member with allocated space/first subobject with exclusively allocated space/;

clang/lib/AST/RecordLayoutBuilder.cpp
1915

Suggestion:

// The AIX `power` alignment rules apply the natural alignment of the
// "first member" if it is of a floating-point data type (or is an aggregate
// whose recursively "first" member or element is such a type). The alignment
// associated with these types for subsequent members use an alignment value
// where the floating-point data type is considered to have 4-byte alignment.
//
// For the purposes of the foregoing: vtable pointers, non-empty base classes,
// and zero-width bit-fields count as prior members; members of empty class
// types marked `no_unique_address` are not considered to be prior members.

This fixes a number of issues with the comment, including:

  • The meaning of "first member" is unclear and the intended meaning is unlikely to be understood from common meanings of the term.
  • The recursive application of the rule was not captured (the relationship is not merely "contains").
  • The statement about 4-byte alignment needed to take stricter alignment due to other factors into account.
clang/lib/AST/RecordLayoutBuilder.cpp
1825

See my other comment for rationale on why HandledFirstNonOverlappingEmptyField should be set to !IsUnion instead of true.

A comment would be appropriate here:

if (FoundNonOverlappingEmptyField) {
  // For a union, the current field does not represent all "firsts".
  HandledFirstNonOverlappingEmptyField = !IsUnion;
}
1923

IsOverlappingEmptyField is still in play (as seen in further checks below). I do not believe it is appropriate to set HandledFirstNonOverlappingEmptyField to true in that case (possible because IsUnion overrides what is currently called FoundNonOverlappingEmptyField).

There are a few ways to address this. For example, HandledFirstNonOverlappingEmptyField could be set to !IsUnion instead for to true. This is semantically sound, because HandledFirstNonOverlappingEmptyField is meant to indicate that there would not be further "firsts" to consider (and that is not true for unions).

I believe the code should not require user to convince themselves that non-sequitur logic washes away, so I would like a fix for this although it introduces no functional change.

clang/lib/AST/RecordLayoutBuilder.cpp
1928

I don't think there's a reason to use getBaseElementType (which is used to handle arrays) on the element type of a ComplexType.

1929

I believe castAs should be expected to succeed here.

1932

I believe an assertion that PreferredAlign was 4 would be appropriate.

1937

Use a lambda instead of duplicating the code.

1942

Is there a reason to use getBaseElementTypeUnsafe for this case and Context.getBaseElementType for the other ones? Also, it would make sense to factor out the array-type considerations once at the top of the if-else chain instead of doing so in each alternative.

1944

I'd be a bit concerned if this failed. Can we assert that we get a non-null pointer back?

Xiangling_L marked 5 inline comments as done.Jul 3 2020, 2:51 PM
Xiangling_L added inline comments.
clang/test/Layout/aix-double-struct-member.cpp
2

Regarding with checking the alignment where we use them, AFAIK the problematic cases include not only the cookie padding issue you mentioned here, but also the alignment of argument type, return type etc.

So I am wondering does it make sense to have them handled in a separate patch since this is already a big one? We can use this patch to implement the correct value of __alignof and alignof and use a second patch to handle the places where we use them where we should?

clang/test/Layout/aix-double-struct-member.cpp
2

Yes, we can scope the patch that way somewhat; however, some cases of "[using the __alignof__ value] where we should" that is missing is within the determination of the base and field offsets. We should keep those within the scope of this patch.

clang/lib/AST/RecordLayoutBuilder.cpp
1921

This if condition does not currently capture that a zero-extent array in a base class renders the base class not empty.

struct Z { char zea[0]; };
struct A {
  Z z [[no_unique_address]];
  double d;
};
struct B : Z { double d; };
static_assert(__alignof__(A) == __alignof__(B));
clang/lib/AST/RecordLayoutBuilder.cpp
1269

Add:
[ ... ] or zero-extent array.

clang/lib/AST/RecordLayoutBuilder.cpp
1950

Testing using my build with this patch seems to indicate that tracking FieldAlign for UnpackedFieldAlign does not lead to the desired result.

The QQ and ZZ cases differ only on the packed attribute on Q. They are observed to have different sizes; however, we get a -Wpacked diagnostic claiming that the packed attribute was unnecessary (and could therefore be removed).

struct [[gnu::packed]] Q {
  double x [[gnu::aligned(4)]];
};
struct QQ : Q { char x; };

struct Z {
  double x [[gnu::aligned(4)]];
};
struct ZZ : Z { char x; };

extern char qx[sizeof(QQ)];
extern char qx[12];
extern char qz[sizeof(ZZ)];
extern char qz[16];
<stdin>:1:24: warning: packed attribute is unnecessary for 'Q' [-Wpacked]
struct [[gnu::packed]] Q {
                       ^
1 warning generated.
clang/lib/AST/ASTContext.cpp
2432

This last question about the ABI align values is relevant to considerations for natural alignment support as well. More generally, the question is whether the "minimum alignment" of the type in a context subject to alternative alignment rules is altered to match said alignment rule. This is observable via the diagnostic associated with C++11 alignment specifiers.

The existing behaviour of mac68k alignment suggests that the "minimum alignment" is context-free.

#pragma options align=mac68k
struct Q {
  double x alignas(2);  // expected-error {{less than minimum alignment}}
};
#pragma options align=reset

Compiler Explorer link: https://godbolt.org/z/9NM5_-

Xiangling_L marked 27 inline comments as done.Jul 6 2020, 7:59 AM
Xiangling_L added inline comments.
clang/lib/AST/ASTContext.cpp
2432

Thank you for your explanation.

clang/lib/AST/RecordLayoutBuilder.cpp
1929

castAs is not declared in current context, do we really want to use it by introducing one more header?

1942

Sorry, I didn't pay attention to the different versions of getBaseElementType functions here and I believe this part of code came from our old compiler's changesets. My understanding would be since type qualifiers are not very meaningful in our case and getBaseElementTypeUnsafe() is more efficient than getBaseElementType(), we can use getBaseElementTypeUnsafe() all over the place instead.

Xiangling_L edited the summary of this revision. (Show Details)Jul 6 2020, 8:47 AM
Xiangling_L marked 3 inline comments as done.EditedJul 6 2020, 8:49 AM

Rebased on the latest master;
Fixed -Wpacked warning issue;
Fixed EmptySubobjects related offset issue;
Fixed zero-extent array in a base class related issue;
Addressed other comments;

clang/lib/AST/ASTContext.cpp
2409

Please add a comment regarding the situations where the ABIAlign value is greater than the PreferredAlignment value. It may be appropriate to assert that, absent those cases, the PreferredAlignment value is at least that of ABIAlign.

2409

It does not appear that the maximum of the two values is the correct answer:

struct C {
  double x;
} c;
typedef struct C __attribute__((__aligned__(2))) CC;

CC cc;
extern char x[__alignof__(cc)];
extern char x[2]; // this is okay with IBM XL C/C++
clang/lib/AST/RecordLayoutBuilder.cpp
1932

It seems that overriding the value should only be done after additional checks:

typedef double __attribute__((__aligned__(2))) Dbl;
struct A {
  Dbl x;
} a;
extern char x[__alignof__(a)];
extern char x[2]; // this is okay with IBM XL C/C++

I am getting concerned that the logic here overlaps quite a bit with getPreferredTypeAlign and refactoring to make the code here more common with getPreferredTypeAlign is necessary.

clang/lib/AST/RecordLayoutBuilder.cpp
1929

castAs should be declared:
https://clang.llvm.org/doxygen/classclang_1_1Type.html#a436b8b08ae7f2404b4712d37986194ce

Also, the additional point is that the if here should be unnecessary. BTy should not be null. We should use castAs to gain the guarantee (with assertions) that BTy is not null.

Xiangling_L marked 6 inline comments as done.

Fixed the typedef related issues;
Added more testcases;

clang/lib/AST/ASTContext.cpp
2409

Please add a comment regarding the situations where the ABIAlign value is greater than the PreferredAlignment value.

I added a if condition to guard the situation where ABIAlign should be returned instead of adding a comment. Please let me know if that is sufficient.

clang/lib/AST/RecordLayoutBuilder.cpp
1932

Fixed the typedef related cases with my new changes, and the overlaps were not a lot as I expected. So I didn't do any refactoring yet. Please let me know if you still think it's necessary to refactor the code somehow.

clang/lib/AST/RecordLayoutBuilder.cpp
666

Minor nit: s/as base/as a base/;

703

Minor nit: Please fix the formatting.

1223

Thanks; verified that this is correct with xlclang++ from IBM XL C/C++ for AIX with:

struct A {
  char x;
};
struct B {
  int x;
};
struct __attribute__((__packed__)) C : A, B {} c;

Length is 5:

[10]    m   0x00000004      .bss     1  extern                    c
[11]    a4  0x00000005       0    0     CM       RW    0    0

@Xiangling_L, I suggest adding a case for this to the tests.

1273

Unions cannot have base classes. Please assert !IsUnion.

1277

Suggestion:

// The maximum field alignment overrides the base align/(AIX-only) preferred
// base align.
clang/test/Layout/aix-power-alignment-typedef.cpp
19

Instead of checking the value of a, the alignment can be checked more directly from the IR for cc.

clang/lib/AST/ASTContext.cpp
2414

Minor nit: s/is/should be/;

clang/lib/AST/RecordLayoutBuilder.cpp
1208

Just set HandledFirstNonOverlappingEmptyField to true with a comment before the if:
By handling a base class that is not empty, we're handling the "first (inherited) member".

1824

Add a comment before the if:

// We're going to handle the "first member" based on
// `FoundNonOverlappingEmptyFieldToHandle` during the current invocation of
// this function; record it as handled for future invocations.

Given the rationale from the comment, move the subject if to immediately after the determination of FoundNonOverlappingEmptyFieldToHandle. That way, the setting of HandledFirstNonOverlappingEmptyField becomes less complicated to track.

1855

It seems that the code to set AlignIsRequired is missing from this path.

typedef double Dbl __attribute__((__aligned__(2)));
typedef Dbl DblArr[];

union U {
  DblArr fam;
  char x;
};

U u[2];
extern char x[sizeof(u)];
extern char x[4];
1928

I think the if condition above is too complicated as a filter for setting HandledFirstNonOverlappingEmptyField. I would prefer if we don't need to set HandledFirstNonOverlappingEmptyField here. Please see my other comment about HandledFirstNonOverlappingEmptyField.

clang/lib/AST/RecordLayoutBuilder.cpp
1930

"Qualified" is a term of art in the context of C/C++ types. Please remove "IfQualified" from the name. The lambda just needs to be named for what it does. When naming it for the conditions where it should be called, "if" (as opposed to "assuming") implies that the function checks the condition itself.

1947

Please don't write a check on a variable right after making an assertion on what its value should be.

Xiangling_L marked 14 inline comments as done.

Fixed typedef issue on incomplete array field and add a test for it;
Added a test for where pack attribute on object also apply on base classes;
Addressed other comments;

clang/lib/AST/RecordLayoutBuilder.cpp
1223

Sure, I will add it.

clang/lib/AST/RecordLayoutBuilder.cpp
1068

I would suggest setting HandledFirstNonOverlappingEmptyField to true here with an assertion that the current type is not a union.

1807

The condition is still more complex than I think it should be.

If we have found a "first" other-than-overlapping-empty-field, then we should set HandledFirstNonOverlappingEmptyField to true for non-union cases.

If HandledFirstNonOverlappingEmptyField being false is not enough for FieldOffset == CharUnits::Zero() to be true, then I think the correction would be to set HandledFirstNonOverlappingEmptyField in more places.

I would like to remove the check on FieldOffset == CharUnits::Zero() from here and instead have an assertion that !HandledFirstNonOverlappingEmptyField implies FieldOffset == CharUnits::Zero().

Also, since we're managing HandledFirstNonOverlappingEmptyField in non-AIX cases, we should remove the DefaultsToAIXPowerAlignment condition for what is currently named FoundFirstNonOverlappingEmptyFieldToHandle (adjusting uses of it as necessary) and rename FoundFirstNonOverlappingEmptyFieldToHandle to FoundFirstNonOverlappingEmptyField.

1854–1855

ATy seems to be an unused variable now.

1856

I guess this works (we have a test for it), but the previous code made a point to use the element type and not the array type (and the comment above says we can't directly query getTypeInfo with the array type). @Xiangling_L, can you confirm if the comment is out-of-date and update it?

1926

It should now be the case that FoundFirstNonOverlappingEmptyFieldToHandle is true for all union members that are not empty, meaning that the IsUnion part of the check only serves to admit attempts to handle types that are empty (and thus does not have subobjects that would induce an alignment upgrade).

clang/lib/AST/RecordLayoutBuilder.cpp
1274

This needs to check HandledFirstNonOverlappingEmptyField:

struct A {
  char x[0];
};
struct B {
  double d;
};
struct C : A, B { char x; } c;
1275

Note that PreferredBaseAlign is only truly meaningful after this line, and related fields, such as UnpackedPreferredBaseAlign, require similar treatment. With UnpackedAlignTo being dependent on a meaningful value of UnpackedPreferredBaseAlign, it needs an update here as well.

Consider the following source. Noting that C and D are identical except for the packed attribute and yield identical layout properties despite that difference, we should be getting a -Wpacked warning with -Wpacked (but it is missing).

struct A {
  double d;
};

struct B {
  char x[8];
};

struct [[gnu::packed]] C : B, A { // expected-warning {{packed attribute is unnecessary}}
  char x alignas(4)[8];
} c;

struct D : B, A {
  char x alignas(4)[8];
} d;
*** Dumping AST Record Layout
         0 | struct C
         0 |   struct B (base)
         0 |     char [8] x
         8 |   struct A (base)
         8 |     double d
        16 |   char [8] x
           | [sizeof=24, dsize=24, align=4, preferredalign=4,
           |  nvsize=24, nvalign=4, preferrednvalign=4]
*** Dumping AST Record Layout
         0 | struct D
         0 |   struct B (base)
         0 |     char [8] x
         8 |   struct A (base)
         8 |     double d
        16 |   char [8] x
           | [sizeof=24, dsize=24, align=4, preferredalign=4,
           |  nvsize=24, nvalign=4, preferrednvalign=4]
Xiangling_L marked 9 inline comments as done.Jul 8 2020, 12:18 PM
Xiangling_L added inline comments.
clang/lib/AST/RecordLayoutBuilder.cpp
1807

Also, since we're managing HandledFirstNonOverlappingEmptyField in non-AIX cases, we should remove the DefaultsToAIXPowerAlignment condition for what is currently named FoundFirstNonOverlappingEmptyFieldToHandle

I am not sure if we want to remove the DefaultsToAIXPowerAlignment condition and bother with maintaining correct status of HandledFirstNonOverlappingEmptyField for other targets.

We are actually claiming HandledFirstNonOverlappingEmptyField is an auxiliary flag used for AIX only in its definition comments.

Besides, if we do want to manage HandledFirstNonOverlappingEmptyField in non-AIX cases, I noticed that we have to set this flag to true somewhere for objective-C++ cases.

1856

I am sure getTypeInfo can recognize the element type for IncompleteArray. I will update the comments.

Xiangling_L marked 2 inline comments as done.

Fixed a -Wpacked related case and added the case to the tests;
Fixed the base class related code issue;
Addressed other comments;

clang/lib/AST/RecordLayoutBuilder.cpp
1807

Okay, the other option I'm open is setting HandledFirstNonOverlappingEmptyField to true up front when not dealing with AIX power alignment.

Xiangling_L marked an inline comment as done.Jul 8 2020, 1:03 PM
Xiangling_L added inline comments.
clang/lib/AST/RecordLayoutBuilder.cpp
1807

Thanks, that works too. I will address it in the next commit.

clang/lib/AST/RecordLayoutBuilder.cpp
1269–1270

It seems this is a leftover copy of the code that has been moved above?

1806

The rename I suggested in my previous round of review was in coordination with maintaining the value not just for AIX. Since we're only maintaining the value for AIX, I prefer the previous name (or FoundFirstNonOverlappingEmptyFieldForAIX).

1807

Please merge the if conditions to reduce nesting:

if (DefaultsToAIXPowerAlignment && !HandledFirstNonOverlappingEmptyField) {
1814

Keep this reference to the variable up-to-date with its name.

1817

Change the condition of the if here to !IsOverlappingEmptyField and move the setting of FoundFirstNonOverlappingEmptyField to true into this if.

Move the previous comment and merge it with this one here:

[ ... ] record it as handled for future invocations (except for unions, because the current field does not represent all "firsts").

1927

Sorry for not seeing this earlier (I only notice some things when I hide the inline comments). I think performBuiltinTypeAlignmentUpgrade would read better at the call site (and better capture the checking, which is based on the kind of built-in type, that is within the lambda).

clang/test/Layout/aix-Wpacked.cpp
9 ↗(On Diff #276528)

Clang diagnostics are normally checked using -verify (as opposed to FileCheck). To use it, I think this needs to be split into the "expecting no diagnostics" and the "expecting diagnostics" cases. As it is, I think the CHECK-NOT has a problem because it checks for plain 'Q'.

clang/lib/AST/RecordLayoutBuilder.cpp
1210

We need some sort of IsFirstNonEmptyBase to record that the current base qualifies for the alignment upgrade:

struct A { double x; };
struct B : A {} b;
1266

Query !IsFirstNonEmptyBase instead of HandledFirstNonOverlappingEmptyField here.

Xiangling_L marked 9 inline comments as done.

Fixed a base class related case by adding IsFirstNonEmpty flag;
Split the aix-Wpacked.cpp testcase into two;
Addressed other comments;

clang/lib/AST/RecordLayoutBuilder.cpp
1246

Move the comment to above the previous if and make the following if the else of the previous if.

1807
clang/test/Layout/aix-Wpacked-expecting-diagnostics.cpp
19

Minor nit: Remove the space before the semicolon.

clang/test/Layout/aix-Wpacked-no-diagnostics.cpp
15

Is there a reason to drop the FileCheck checking for the layout?

Xiangling_L marked 6 inline comments as done.

Set Handled... = true for non-AIX power alignment;
Addressed other comments;

clang/lib/AST/RecordLayoutBuilder.cpp
1244

IsFirstNonEmptyBase can be removed. It is set, but not used.

Removed unused var;

Xiangling_L added inline comments.Jul 13 2020, 7:46 AM
clang/test/Layout/aix-Wpacked-no-diagnostics.cpp
15

I dropped the FileCheck because the layout of QQ and Q are fairly simple and wanted the test focus more on no-diagnostics side. But I can add it back if that helps.

I've done another pass over the code (but did not get through the tests). I have no comments about the code at this time. My understanding is that @jasonliu will be doing another pass over this patch, so he can approve while I'm away on vacation.

jasonliu added inline comments.Jul 22 2020, 11:32 AM
clang/lib/AST/ASTContext.cpp
2418

Should this if statement go above the if (const auto *RT = T->getAs<RecordType>()) ?
When Target does not allow larger prefered type alignment, then we should return ABIAlign immediately without going through the RecordType query?

clang/lib/AST/RecordLayoutBuilder.cpp
1235

Add const?

1804

const ?

clang/lib/Basic/Targets/PPC.h
425

SuitableAlign is set in line 412 as well. Please consider combining the two if statements if grouping things together makes code easier to parse.

clang/test/Layout/aix-double-struct-member.cpp
3

You are not using < %s here. So -x c++ is redundant?

306

b is not necessary when you take the sizeof of B below?

clang/test/Layout/aix-no-unique-address-with-double.cpp
139

I think this case is interesting and may worth adding too:

struct F {
  [[no_unique_address]] Empty emp, emp2;
  double d;
};
clang/test/Layout/aix-power-alignment-typedef.cpp
13

remove c ?

26

remove a?

44

remove u?

Xiangling_L marked 13 inline comments as done.Jul 22 2020, 1:16 PM
Xiangling_L added inline comments.
clang/lib/AST/ASTContext.cpp
2418

Agree. I will update this.

clang/test/Layout/aix-double-struct-member.cpp
3

Yeah, thanks, I will remove it.

clang/test/Layout/aix-no-unique-address-with-double.cpp
139

Sure.

Xiangling_L marked 3 inline comments as done.

Add one more testcase;
Addressed other comments;

Thanks. No further comments from me.

jasonliu accepted this revision.Jul 22 2020, 1:37 PM
  • Simplified the test command line;
  • Split the typedef related tests into two to address the LIT testcase failure on windows platform;
This revision was automatically updated to reflect the committed changes.
ebevhan added a subscriber: ebevhan.Aug 3 2020, 4:43 AM
ebevhan added inline comments.
clang/lib/AST/RecordLayoutBuilder.cpp
1779

It seems that in factoring out this to setDeclInfo, it was changed from using getTypeInfoInChars to using getTypeInfo+toCharUnitsFromBits. This is causing some downstream issues for us with non-bytesize-multiple types.

Changing setDeclInfo to use the original function instead seems to work without issues. Would it be acceptable to change this?

efriedma added inline comments.Aug 3 2020, 11:41 AM
clang/lib/AST/RecordLayoutBuilder.cpp
1779

In general, we want to avoid representing type size/alignment in bits where it isn't necessary; refactoring along those lines is welcome.

I submitted a patch with the changes at D85191.