diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/CodeGen/CGExprAgg.cpp | 73 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprConstant.cpp | 107 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.h | 51 |
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; |