aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/CodeGen/CGExprAgg.cpp73
-rw-r--r--clang/lib/CodeGen/CGExprConstant.cpp107
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h51
3 files changed, 218 insertions, 13 deletions
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 74d4c5e..2ad6587 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -12,6 +12,7 @@
#include "CGCXXABI.h"
#include "CGObjCRuntime.h"
+#include "CGRecordLayout.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "ConstantEmitter.h"
@@ -64,6 +65,9 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
void withReturnValueSlot(const Expr *E,
llvm::function_ref<RValue(ReturnValueSlot)> Fn);
+ void DoZeroInitPadding(uint64_t &PaddingStart, uint64_t PaddingEnd,
+ const FieldDecl *NextField);
+
public:
AggExprEmitter(CodeGenFunction &cgf, AggValueSlot Dest, bool IsResultUnused)
: CGF(cgf), Builder(CGF.Builder), Dest(Dest),
@@ -1698,6 +1702,9 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
// Prepare a 'this' for CXXDefaultInitExprs.
CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddress());
+ const bool ZeroInitPadding =
+ CGF.CGM.shouldZeroInitPadding() && !Dest.isZeroed();
+
if (record->isUnion()) {
// Only initialize one field of a union. The field itself is
// specified by the initializer list.
@@ -1722,16 +1729,27 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
if (NumInitElements) {
// Store the initializer into the field
EmitInitializationToLValue(InitExprs[0], FieldLoc);
+ if (ZeroInitPadding) {
+ uint64_t TotalSize = CGF.getContext().toBits(
+ Dest.getPreferredSize(CGF.getContext(), DestLV.getType()));
+ uint64_t FieldSize = CGF.getContext().getTypeSize(FieldLoc.getType());
+ DoZeroInitPadding(FieldSize, TotalSize, nullptr);
+ }
} else {
// Default-initialize to null.
- EmitNullInitializationToLValue(FieldLoc);
+ if (ZeroInitPadding)
+ EmitNullInitializationToLValue(DestLV);
+ else
+ EmitNullInitializationToLValue(FieldLoc);
}
-
return;
}
// Here we iterate over the fields; this makes it simpler to both
// default-initialize fields and skip over unnamed fields.
+ const ASTRecordLayout &Layout = CGF.getContext().getASTRecordLayout(record);
+ uint64_t PaddingStart = 0;
+
for (const auto *field : record->fields()) {
// We're done once we hit the flexible array member.
if (field->getType()->isIncompleteArrayType())
@@ -1748,6 +1766,9 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
CGF.getTypes().isZeroInitializable(ExprToVisit->getType()))
break;
+ if (ZeroInitPadding)
+ DoZeroInitPadding(PaddingStart,
+ Layout.getFieldOffset(field->getFieldIndex()), field);
LValue LV = CGF.EmitLValueForFieldInitialization(DestLV, field);
// We never generate write-barries for initialized fields.
@@ -1774,6 +1795,54 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
}
}
}
+ if (ZeroInitPadding) {
+ uint64_t TotalSize = CGF.getContext().toBits(
+ Dest.getPreferredSize(CGF.getContext(), DestLV.getType()));
+ DoZeroInitPadding(PaddingStart, TotalSize, nullptr);
+ }
+}
+
+void AggExprEmitter::DoZeroInitPadding(uint64_t &PaddingStart,
+ uint64_t PaddingEnd,
+ const FieldDecl *NextField) {
+
+ auto InitBytes = [&](uint64_t StartBit, uint64_t EndBit) {
+ CharUnits Start = CGF.getContext().toCharUnitsFromBits(StartBit);
+ CharUnits End = CGF.getContext().toCharUnitsFromBits(EndBit);
+ Address Addr = Dest.getAddress().withElementType(CGF.CharTy);
+ if (!Start.isZero())
+ Addr = Builder.CreateConstGEP(Addr, Start.getQuantity());
+ llvm::Constant *SizeVal = Builder.getInt64((End - Start).getQuantity());
+ CGF.Builder.CreateMemSet(Addr, Builder.getInt8(0), SizeVal, false);
+ };
+
+ if (NextField != nullptr && NextField->isBitField()) {
+ // For bitfield, zero init StorageSize before storing the bits. So we don't
+ // need to handle big/little endian.
+ const CGRecordLayout &RL =
+ CGF.getTypes().getCGRecordLayout(NextField->getParent());
+ const CGBitFieldInfo &Info = RL.getBitFieldInfo(NextField);
+ uint64_t StorageStart = CGF.getContext().toBits(Info.StorageOffset);
+ if (StorageStart + Info.StorageSize > PaddingStart) {
+ if (StorageStart > PaddingStart)
+ InitBytes(PaddingStart, StorageStart);
+ Address Addr = Dest.getAddress();
+ if (!Info.StorageOffset.isZero())
+ Addr = Builder.CreateConstGEP(Addr.withElementType(CGF.CharTy),
+ Info.StorageOffset.getQuantity());
+ Addr = Addr.withElementType(
+ llvm::Type::getIntNTy(CGF.getLLVMContext(), Info.StorageSize));
+ Builder.CreateStore(Builder.getIntN(Info.StorageSize, 0), Addr);
+ PaddingStart = StorageStart + Info.StorageSize;
+ }
+ return;
+ }
+
+ if (PaddingStart < PaddingEnd)
+ InitBytes(PaddingStart, PaddingEnd);
+ if (NextField != nullptr)
+ PaddingStart =
+ PaddingEnd + CGF.getContext().getTypeSize(NextField->getType());
}
void AggExprEmitter::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E,
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index dd65080..655fc3d 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -42,6 +42,16 @@ using namespace CodeGen;
namespace {
class ConstExprEmitter;
+llvm::Constant *getPadding(const CodeGenModule &CGM, CharUnits PadSize) {
+ llvm::Type *Ty = CGM.CharTy;
+ if (PadSize > CharUnits::One())
+ Ty = llvm::ArrayType::get(Ty, PadSize.getQuantity());
+ if (CGM.shouldZeroInitPadding()) {
+ return llvm::Constant::getNullValue(Ty);
+ }
+ return llvm::UndefValue::get(Ty);
+}
+
struct ConstantAggregateBuilderUtils {
CodeGenModule &CGM;
@@ -61,10 +71,7 @@ struct ConstantAggregateBuilderUtils {
}
llvm::Constant *getPadding(CharUnits PadSize) const {
- llvm::Type *Ty = CGM.CharTy;
- if (PadSize > CharUnits::One())
- Ty = llvm::ArrayType::get(Ty, PadSize.getQuantity());
- return llvm::UndefValue::get(Ty);
+ return ::getPadding(CGM, PadSize);
}
llvm::Constant *getZeroes(CharUnits ZeroSize) const {
@@ -591,6 +598,11 @@ private:
bool Build(const InitListExpr *ILE, bool AllowOverwrite);
bool Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase,
const CXXRecordDecl *VTableClass, CharUnits BaseOffset);
+ bool DoZeroInitPadding(const ASTRecordLayout &Layout, unsigned FieldNo,
+ const FieldDecl &Field, bool AllowOverwrite,
+ CharUnits &SizeSoFar, bool &ZeroFieldSize);
+ bool DoZeroInitPadding(const ASTRecordLayout &Layout, bool AllowOverwrite,
+ CharUnits SizeSoFar);
llvm::Constant *Finalize(QualType Ty);
};
@@ -715,6 +727,10 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
if (CXXRD->getNumBases())
return false;
+ const bool ZeroInitPadding = CGM.shouldZeroInitPadding();
+ bool ZeroFieldSize = false;
+ CharUnits SizeSoFar = CharUnits::Zero();
+
for (FieldDecl *Field : RD->fields()) {
++FieldNo;
@@ -732,8 +748,13 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
const Expr *Init = nullptr;
if (ElementNo < ILE->getNumInits())
Init = ILE->getInit(ElementNo++);
- if (isa_and_nonnull<NoInitExpr>(Init))
+ if (isa_and_nonnull<NoInitExpr>(Init)) {
+ if (ZeroInitPadding &&
+ !DoZeroInitPadding(Layout, FieldNo, *Field, AllowOverwrite, SizeSoFar,
+ ZeroFieldSize))
+ return false;
continue;
+ }
// Zero-sized fields are not emitted, but their initializers may still
// prevent emission of this struct as a constant.
@@ -743,6 +764,11 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
continue;
}
+ if (ZeroInitPadding &&
+ !DoZeroInitPadding(Layout, FieldNo, *Field, AllowOverwrite, SizeSoFar,
+ ZeroFieldSize))
+ return false;
+
// When emitting a DesignatedInitUpdateExpr, a nested InitListExpr
// represents additional overwriting of our current constant value, and not
// a new constant to emit independently.
@@ -768,6 +794,10 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
if (!EltInit)
return false;
+ if (ZeroInitPadding && ZeroFieldSize)
+ SizeSoFar += CharUnits::fromQuantity(
+ CGM.getDataLayout().getTypeAllocSize(EltInit->getType()));
+
if (!Field->isBitField()) {
// Handle non-bitfield members.
if (!AppendField(Field, Layout.getFieldOffset(FieldNo), EltInit,
@@ -785,6 +815,9 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
}
}
+ if (ZeroInitPadding && !DoZeroInitPadding(Layout, AllowOverwrite, SizeSoFar))
+ return false;
+
return true;
}
@@ -849,6 +882,9 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
unsigned FieldNo = 0;
uint64_t OffsetBits = CGM.getContext().toBits(Offset);
+ const bool ZeroInitPadding = CGM.shouldZeroInitPadding();
+ bool ZeroFieldSize = false;
+ CharUnits SizeSoFar = CharUnits::Zero();
bool AllowOverwrite = false;
for (RecordDecl::field_iterator Field = RD->field_begin(),
@@ -870,6 +906,15 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
if (!EltInit)
return false;
+ if (ZeroInitPadding) {
+ if (!DoZeroInitPadding(Layout, FieldNo, **Field, AllowOverwrite,
+ SizeSoFar, ZeroFieldSize))
+ return false;
+ if (ZeroFieldSize)
+ SizeSoFar += CharUnits::fromQuantity(
+ CGM.getDataLayout().getTypeAllocSize(EltInit->getType()));
+ }
+
if (!Field->isBitField()) {
// Handle non-bitfield members.
if (!AppendField(*Field, Layout.getFieldOffset(FieldNo) + OffsetBits,
@@ -886,7 +931,49 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
return false;
}
}
+ if (ZeroInitPadding && !DoZeroInitPadding(Layout, AllowOverwrite, SizeSoFar))
+ return false;
+
+ return true;
+}
+
+bool ConstStructBuilder::DoZeroInitPadding(
+ const ASTRecordLayout &Layout, unsigned FieldNo, const FieldDecl &Field,
+ bool AllowOverwrite, CharUnits &SizeSoFar, bool &ZeroFieldSize) {
+ uint64_t StartBitOffset = Layout.getFieldOffset(FieldNo);
+ CharUnits StartOffset = CGM.getContext().toCharUnitsFromBits(StartBitOffset);
+ if (SizeSoFar < StartOffset)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, StartOffset - SizeSoFar),
+ AllowOverwrite))
+ return false;
+
+ if (!Field.isBitField()) {
+ CharUnits FieldSize = CGM.getContext().getTypeSizeInChars(Field.getType());
+ SizeSoFar = StartOffset + FieldSize;
+ ZeroFieldSize = FieldSize.isZero();
+ } else {
+ const CGRecordLayout &RL =
+ CGM.getTypes().getCGRecordLayout(Field.getParent());
+ const CGBitFieldInfo &Info = RL.getBitFieldInfo(&Field);
+ uint64_t EndBitOffset = StartBitOffset + Info.Size;
+ SizeSoFar = CGM.getContext().toCharUnitsFromBits(EndBitOffset);
+ if (EndBitOffset % CGM.getContext().getCharWidth() != 0) {
+ SizeSoFar++;
+ }
+ ZeroFieldSize = Info.Size == 0;
+ }
+ return true;
+}
+bool ConstStructBuilder::DoZeroInitPadding(const ASTRecordLayout &Layout,
+ bool AllowOverwrite,
+ CharUnits SizeSoFar) {
+ CharUnits TotalSize = Layout.getSize();
+ if (SizeSoFar < TotalSize)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, TotalSize - SizeSoFar),
+ AllowOverwrite))
+ return false;
+ SizeSoFar = TotalSize;
return true;
}
@@ -1127,12 +1214,10 @@ public:
assert(CurSize <= TotalSize && "Union size mismatch!");
if (unsigned NumPadBytes = TotalSize - CurSize) {
- llvm::Type *Ty = CGM.CharTy;
- if (NumPadBytes > 1)
- Ty = llvm::ArrayType::get(Ty, NumPadBytes);
-
- Elts.push_back(llvm::UndefValue::get(Ty));
- Types.push_back(Ty);
+ llvm::Constant *Padding =
+ getPadding(CGM, CharUnits::fromQuantity(NumPadBytes));
+ Elts.push_back(Padding);
+ Types.push_back(Padding->getType());
}
llvm::StructType *STy = llvm::StructType::get(VMContext, Types, false);
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 57e06cb..fa82a81 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1687,6 +1687,57 @@ public:
MustTailCallUndefinedGlobals.insert(Global);
}
+ bool shouldZeroInitPadding() const {
+ // In C23 (N3096) $6.7.10:
+ // """
+ // If any object is initialized with an empty iniitializer, then it is
+ // subject to default initialization:
+ // - if it is an aggregate, every member is initialized (recursively)
+ // according to these rules, and any padding is initialized to zero bits;
+ // - if it is a union, the first named member is initialized (recursively)
+ // according to these rules, and any padding is initialized to zero bits.
+ //
+ // If the aggregate or union contains elements or members that are
+ // aggregates or unions, these rules apply recursively to the subaggregates
+ // or contained unions.
+ //
+ // If there are fewer initializers in a brace-enclosed list than there are
+ // elements or members of an aggregate, or fewer characters in a string
+ // literal used to initialize an array of known size than there are elements
+ // in the array, the remainder of the aggregate is subject to default
+ // initialization.
+ // """
+ //
+ // From my understanding, the standard is ambiguous in the following two
+ // areas:
+ // 1. For a union type with empty initializer, if the first named member is
+ // not the largest member, then the bytes comes after the first named member
+ // but before padding are left unspecified. An example is:
+ // union U { int a; long long b;};
+ // union U u = {}; // The first 4 bytes are 0, but 4-8 bytes are left
+ // unspecified.
+ //
+ // 2. It only mentions padding for empty initializer, but doesn't mention
+ // padding for a non empty initialization list. And if the aggregation or
+ // union contains elements or members that are aggregates or unions, and
+ // some are non empty initializers, while others are empty initiailizers,
+ // the padding initialization is unclear. An example is:
+ // struct S1 { int a; long long b; };
+ // struct S2 { char c; struct S1 s1; };
+ // // The values for paddings between s2.c and s2.s1.a, between s2.s1.a
+ // and s2.s1.b are unclear.
+ // struct S2 s2 = { 'c' };
+ //
+ // Here we choose to zero initiailize left bytes of a union type. Because
+ // projects like the Linux kernel are relying on this behavior. If we don't
+ // explicitly zero initialize them, the undef values can be optimized to
+ // return gabage data. We also choose to zero initialize paddings for
+ // aggregates and unions, no matter they are initialized by empty
+ // initializers or non empty initializers. This can provide a consistent
+ // behavior. So projects like the Linux kernel can rely on it.
+ return !getLangOpts().CPlusPlus;
+ }
+
private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;