Skip to content

Commit 4802edd

Browse files
committed
Fix size of flexible array initializers, and re-enable assertions.
In D123649, I got the formula for getFlexibleArrayInitChars slightly wrong: the flexible array elements can be contained in the tail padding of the struct. Fix the formula to account for that. With the fixed formula, we run into another issue: in some cases, we were emitting extra padding for flexible arrray initializers. Fix CGExprConstant so it uses a packed struct when necessary, to avoid this extra padding. Differential Revision: https://reviews.llvm.org/D123826
1 parent dc100eb commit 4802edd

File tree

7 files changed

+58
-26
lines changed

7 files changed

+58
-26
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,12 +1591,18 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
15911591
/// kind?
15921592
QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const;
15931593

1594-
/// If this variable declares a struct with a flexible array member, and
1595-
/// the flexible array member is initialized with one or more elements,
1596-
/// compute the number of bytes necessary to store those elements.
1594+
/// Whether this variable has a flexible array member initialized with one
1595+
/// or more elements. This can only be called for declarations where
1596+
/// hasInit() is true.
15971597
///
15981598
/// (The standard doesn't allow initializing flexible array members; this is
15991599
/// a gcc/msvc extension.)
1600+
bool hasFlexibleArrayInit(const ASTContext &Ctx) const;
1601+
1602+
/// If hasFlexibleArrayInit is true, compute the number of additional bytes
1603+
/// necessary to store those elements. Otherwise, returns zero.
1604+
///
1605+
/// This can only be called for declarations where hasInit() is true.
16001606
CharUnits getFlexibleArrayInitChars(const ASTContext &Ctx) const;
16011607

16021608
// Implement isa/cast/dyncast/etc.

clang/lib/AST/Decl.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "clang/AST/PrettyDeclStackTrace.h"
3232
#include "clang/AST/PrettyPrinter.h"
3333
#include "clang/AST/Randstruct.h"
34+
#include "clang/AST/RecordLayout.h"
3435
#include "clang/AST/Redeclarable.h"
3536
#include "clang/AST/Stmt.h"
3637
#include "clang/AST/TemplateBase.h"
@@ -2722,6 +2723,21 @@ VarDecl::needsDestruction(const ASTContext &Ctx) const {
27222723
return getType().isDestructedType();
27232724
}
27242725

2726+
bool VarDecl::hasFlexibleArrayInit(const ASTContext &Ctx) const {
2727+
assert(hasInit() && "Expect initializer to check for flexible array init");
2728+
auto *Ty = getType()->getAs<RecordType>();
2729+
if (!Ty || !Ty->getDecl()->hasFlexibleArrayMember())
2730+
return false;
2731+
auto *List = dyn_cast<InitListExpr>(getInit()->IgnoreParens());
2732+
if (!List)
2733+
return false;
2734+
const Expr *FlexibleInit = List->getInit(List->getNumInits() - 1);
2735+
auto InitTy = Ctx.getAsConstantArrayType(FlexibleInit->getType());
2736+
if (!InitTy)
2737+
return false;
2738+
return InitTy->getSize() != 0;
2739+
}
2740+
27252741
CharUnits VarDecl::getFlexibleArrayInitChars(const ASTContext &Ctx) const {
27262742
assert(hasInit() && "Expect initializer to check for flexible array init");
27272743
auto *Ty = getType()->getAs<RecordType>();
@@ -2730,11 +2746,17 @@ CharUnits VarDecl::getFlexibleArrayInitChars(const ASTContext &Ctx) const {
27302746
auto *List = dyn_cast<InitListExpr>(getInit()->IgnoreParens());
27312747
if (!List)
27322748
return CharUnits::Zero();
2733-
auto FlexibleInit = List->getInit(List->getNumInits() - 1);
2749+
const Expr *FlexibleInit = List->getInit(List->getNumInits() - 1);
27342750
auto InitTy = Ctx.getAsConstantArrayType(FlexibleInit->getType());
27352751
if (!InitTy)
27362752
return CharUnits::Zero();
2737-
return Ctx.getTypeSizeInChars(InitTy);
2753+
CharUnits FlexibleArraySize = Ctx.getTypeSizeInChars(InitTy);
2754+
const ASTRecordLayout &RL = Ctx.getASTRecordLayout(Ty->getDecl());
2755+
CharUnits FlexibleArrayOffset =
2756+
Ctx.toCharUnitsFromBits(RL.getFieldOffset(RL.getFieldCount() - 1));
2757+
if (FlexibleArrayOffset + FlexibleArraySize < RL.getSize())
2758+
return CharUnits::Zero();
2759+
return FlexibleArrayOffset + FlexibleArraySize - RL.getSize();
27382760
}
27392761

27402762
MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
342342
if (!Init) {
343343
if (!getLangOpts().CPlusPlus)
344344
CGM.ErrorUnsupported(D.getInit(), "constant l-value expression");
345-
else if (!D.getFlexibleArrayInitChars(getContext()).isZero())
345+
else if (D.hasFlexibleArrayInit(getContext()))
346346
CGM.ErrorUnsupported(D.getInit(), "flexible array initializer");
347347
else if (HaveInsertPoint()) {
348348
// Since we have a static initializer, this global variable can't
@@ -354,17 +354,12 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
354354
return GV;
355355
}
356356

357-
#if 0
358-
// FIXME: The following check doesn't handle flexible array members
359-
// inside tail padding (which don't actually increase the size of
360-
// the struct).
361357
#ifndef NDEBUG
362358
CharUnits VarSize = CGM.getContext().getTypeSizeInChars(D.getType()) +
363359
D.getFlexibleArrayInitChars(getContext());
364360
CharUnits CstSize = CharUnits::fromQuantity(
365361
CGM.getDataLayout().getTypeAllocSize(Init->getType()));
366362
assert(VarSize == CstSize && "Emitted constant has unexpected size");
367-
#endif
368363
#endif
369364

370365
// The initializer may differ in type from the global. Rewrite

clang/lib/CodeGen/CGExprConstant.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -439,22 +439,33 @@ llvm::Constant *ConstantAggregateBuilder::buildFrom(
439439
// Can't emit as an array, carry on to emit as a struct.
440440
}
441441

442+
// The size of the constant we plan to generate. This is usually just
443+
// the size of the initialized type, but in AllowOversized mode (i.e.
444+
// flexible array init), it can be larger.
442445
CharUnits DesiredSize = Utils.getSize(DesiredTy);
446+
if (Size > DesiredSize) {
447+
assert(AllowOversized && "Elems are oversized");
448+
DesiredSize = Size;
449+
}
450+
451+
// The natural alignment of an unpacked LLVM struct with the given elements.
443452
CharUnits Align = CharUnits::One();
444453
for (llvm::Constant *C : Elems)
445454
Align = std::max(Align, Utils.getAlignment(C));
455+
456+
// The natural size of an unpacked LLVM struct with the given elements.
446457
CharUnits AlignedSize = Size.alignTo(Align);
447458

448459
bool Packed = false;
449460
ArrayRef<llvm::Constant*> UnpackedElems = Elems;
450461
llvm::SmallVector<llvm::Constant*, 32> UnpackedElemStorage;
451-
if ((DesiredSize < AlignedSize && !AllowOversized) ||
452-
DesiredSize.alignTo(Align) != DesiredSize) {
453-
// The natural layout would be the wrong size; force use of a packed layout.
462+
if (DesiredSize < AlignedSize || DesiredSize.alignTo(Align) != DesiredSize) {
463+
// The natural layout would be too big; force use of a packed layout.
454464
NaturalLayout = false;
455465
Packed = true;
456466
} else if (DesiredSize > AlignedSize) {
457-
// The constant would be too small. Add padding to fix it.
467+
// The natural layout would be too small. Add padding to fix it. (This
468+
// is ignored if we choose a packed layout.)
458469
UnpackedElemStorage.assign(Elems.begin(), Elems.end());
459470
UnpackedElemStorage.push_back(Utils.getPadding(DesiredSize - Size));
460471
UnpackedElems = UnpackedElemStorage;
@@ -482,7 +493,7 @@ llvm::Constant *ConstantAggregateBuilder::buildFrom(
482493
// If we're using the packed layout, pad it out to the desired size if
483494
// necessary.
484495
if (Packed) {
485-
assert((SizeSoFar <= DesiredSize || AllowOversized) &&
496+
assert(SizeSoFar <= DesiredSize &&
486497
"requested size is too small for contents");
487498
if (SizeSoFar < DesiredSize)
488499
PackedElems.push_back(Utils.getPadding(DesiredSize - SizeSoFar));

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4615,7 +4615,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
46154615
T = D->getType();
46164616

46174617
if (getLangOpts().CPlusPlus) {
4618-
if (!InitDecl->getFlexibleArrayInitChars(getContext()).isZero())
4618+
if (InitDecl->hasFlexibleArrayInit(getContext()))
46194619
ErrorUnsupported(D, "flexible array initializer");
46204620
Init = EmitNullConstant(T);
46214621
NeedsGlobalCtor = true;
@@ -4631,17 +4631,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
46314631
if (getLangOpts().CPlusPlus && !NeedsGlobalDtor)
46324632
DelayedCXXInitPosition.erase(D);
46334633

4634-
#if 0
4635-
// FIXME: The following check doesn't handle flexible array members
4636-
// inside tail padding (which don't actually increase the size of
4637-
// the struct).
46384634
#ifndef NDEBUG
46394635
CharUnits VarSize = getContext().getTypeSizeInChars(ASTTy) +
46404636
InitDecl->getFlexibleArrayInitChars(getContext());
46414637
CharUnits CstSize = CharUnits::fromQuantity(
46424638
getDataLayout().getTypeAllocSize(Init->getType()));
46434639
assert(VarSize == CstSize && "Emitted constant has unexpected size");
4644-
#endif
46454640
#endif
46464641
}
46474642
}

clang/test/CodeGen/flexible-array-init.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,5 @@ struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } d = { 1, 2
1818
struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } e = { 1, 2, { 13, 15, 17, 19 } };
1919
// CHECK: @e ={{.*}} <{ i8, i32, [4 x i8] }> <{ i8 1, i32 2, [4 x i8] c"\0D\0F\11\13" }>
2020

21-
// FIXME: This global should be 6 bytes, not 8. Not likely to matter in most
22-
// cases, but still a bug.
2321
struct { int x; char y[]; } f = { 1, { 13, 15 } };
24-
// CHECK: @f ={{.*}} global { i32, [2 x i8] } { i32 1, [2 x i8] c"\0D\0F" }
22+
// CHECK: @f ={{.*}} global <{ i32, [2 x i8] }> <{ i32 1, [2 x i8] c"\0D\0F" }>

clang/test/CodeGenCXX/flexible-array-init.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o - %s | FileCheck %s
22
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm-only -verify -DFAIL1 %s
33
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm-only -verify -DFAIL2 %s
4+
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm-only -verify -DFAIL3 %s
45

56
struct A { int x; int y[]; };
67
A a = { 1, 7, 11 };
@@ -9,7 +10,7 @@ A a = { 1, 7, 11 };
910
A b = { 1, { 13, 15 } };
1011
// CHECK: @b ={{.*}} global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 13, i32 15] }
1112

12-
int f();
13+
char f();
1314
#ifdef FAIL1
1415
A c = { f(), { f(), f() } }; // expected-error {{cannot compile this flexible array initializer yet}}
1516
#endif
@@ -18,3 +19,7 @@ void g() {
1819
static A d = { f(), { f(), f() } }; // expected-error {{cannot compile this flexible array initializer yet}}
1920
}
2021
#endif
22+
#ifdef FAIL3
23+
struct B { int x; char y; char z[]; };
24+
B e = {f(), f(), f(), f()}; // expected-error {{cannot compile this flexible array initializer yet}}
25+
#endif

0 commit comments

Comments
 (0)