diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -359,20 +359,23 @@ unsigned IsVolatile : 1; unsigned IsUnused : 1; unsigned IsExternallyDestructed : 1; + unsigned IsDummy : 1; public: ReturnValueSlot() - : IsVolatile(false), IsUnused(false), IsExternallyDestructed(false) {} + : IsVolatile(false), IsUnused(false), IsExternallyDestructed(false), + IsDummy(false) {} ReturnValueSlot(Address Addr, bool IsVolatile, bool IsUnused = false, - bool IsExternallyDestructed = false) + bool IsExternallyDestructed = false, bool IsDummy = false) : Addr(Addr), IsVolatile(IsVolatile), IsUnused(IsUnused), - IsExternallyDestructed(IsExternallyDestructed) {} + IsExternallyDestructed(IsExternallyDestructed), IsDummy(IsDummy) {} bool isNull() const { return !Addr.isValid(); } bool isVolatile() const { return IsVolatile; } Address getValue() const { return Addr; } bool isUnused() const { return IsUnused; } bool isExternallyDestructed() const { return IsExternallyDestructed; } + bool isDummy() const { return IsDummy; } }; /// Helper to add attributes to \p F according to the CodeGenOptions and diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5778,8 +5778,10 @@ } // If the value is offset in memory, apply the offset now. - Address StorePtr = emitAddressAtOffset(*this, DestPtr, RetAI); - CreateCoercedStore(CI, StorePtr, DestIsVolatile, *this); + if (!ReturnValue.isDummy()) { + Address StorePtr = emitAddressAtOffset(*this, DestPtr, RetAI); + CreateCoercedStore(CI, StorePtr, DestIsVolatile, *this); + } return convertTempToRValue(DestPtr, RetTy, SourceLocation()); } diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "ABIInfoImpl.h" #include "CGBlocks.h" #include "CGCXXABI.h" #include "CGDebugInfo.h" @@ -695,12 +696,15 @@ EmitComplexExprIntoLValue(Init, LHS, /*isInit*/ true); break; case TEK_Aggregate: { + bool IsDummy = Field->hasAttr() && + isEmptyRecord(getContext(), FieldType, true); AggValueSlot Slot = AggValueSlot::forLValue( LHS, *this, AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased, getOverlapForFieldInit(Field), AggValueSlot::IsNotZeroed, // Checks are made by the code that calls constructor. - AggValueSlot::IsSanitizerChecked); + AggValueSlot::IsSanitizerChecked, + IsDummy ? AggValueSlot::IsADummySlot : AggValueSlot::IsNotADummySlot); EmitAggExpr(Init, Slot); break; } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -317,7 +317,7 @@ RValue Src = EmitCall(ReturnValueSlot(RetAddr, Dest.isVolatile(), IsResultUnused, - Dest.isExternallyDestructed())); + Dest.isExternallyDestructed(), Dest.isDummy())); if (!UseTemp) return; diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h --- a/clang/lib/CodeGen/CGValue.h +++ b/clang/lib/CodeGen/CGValue.h @@ -559,13 +559,16 @@ /// them. bool SanitizerCheckedFlag : 1; + bool IsDummy : 1; + +public: AggValueSlot(Address Addr, Qualifiers Quals, bool DestructedFlag, bool ObjCGCFlag, bool ZeroedFlag, bool AliasedFlag, - bool OverlapFlag, bool SanitizerCheckedFlag) + bool OverlapFlag, bool SanitizerCheckedFlag, bool IsDummy) : Addr(Addr), Quals(Quals), DestructedFlag(DestructedFlag), ObjCGCFlag(ObjCGCFlag), ZeroedFlag(ZeroedFlag), AliasedFlag(AliasedFlag), OverlapFlag(OverlapFlag), - SanitizerCheckedFlag(SanitizerCheckedFlag) {} + SanitizerCheckedFlag(SanitizerCheckedFlag), IsDummy(IsDummy) {} public: enum IsAliased_t { IsNotAliased, IsAliased }; @@ -574,6 +577,7 @@ enum Overlap_t { DoesNotOverlap, MayOverlap }; enum NeedsGCBarriers_t { DoesNotNeedGCBarriers, NeedsGCBarriers }; enum IsSanitizerChecked_t { IsNotSanitizerChecked, IsSanitizerChecked }; + enum IsDummy_t { IsNotADummySlot, IsADummySlot }; /// ignored - Returns an aggregate value slot indicating that the /// aggregate value is being ignored. @@ -592,27 +596,26 @@ /// for calling destructors on this object /// \param needsGC - true if the slot is potentially located /// somewhere that ObjC GC calls should be emitted for - static AggValueSlot forAddr(Address addr, - Qualifiers quals, - IsDestructed_t isDestructed, - NeedsGCBarriers_t needsGC, - IsAliased_t isAliased, - Overlap_t mayOverlap, - IsZeroed_t isZeroed = IsNotZeroed, - IsSanitizerChecked_t isChecked = IsNotSanitizerChecked) { + static AggValueSlot + forAddr(Address addr, Qualifiers quals, IsDestructed_t isDestructed, + NeedsGCBarriers_t needsGC, IsAliased_t isAliased, + Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed, + IsSanitizerChecked_t isChecked = IsNotSanitizerChecked, + IsDummy_t isDummy = IsNotADummySlot) { if (addr.isValid()) addr.setKnownNonNull(); return AggValueSlot(addr, quals, isDestructed, needsGC, isZeroed, isAliased, - mayOverlap, isChecked); + mayOverlap, isChecked, isDummy); } static AggValueSlot forLValue(const LValue &LV, CodeGenFunction &CGF, IsDestructed_t isDestructed, NeedsGCBarriers_t needsGC, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed, - IsSanitizerChecked_t isChecked = IsNotSanitizerChecked) { + IsSanitizerChecked_t isChecked = IsNotSanitizerChecked, + IsDummy_t isDummy = IsNotADummySlot) { return forAddr(LV.getAddress(CGF), LV.getQuals(), isDestructed, needsGC, - isAliased, mayOverlap, isZeroed, isChecked); + isAliased, mayOverlap, isZeroed, isChecked, isDummy); } IsDestructed_t isExternallyDestructed() const { @@ -671,6 +674,8 @@ return SanitizerCheckedFlag; } + bool isDummy() const { return IsDummy; } + RValue asRValue() const { if (isIgnored()) { return RValue::getIgnored(); diff --git a/clang/test/CodeGenCXX/ctor-empty-nounique.cpp b/clang/test/CodeGenCXX/ctor-empty-nounique.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/ctor-empty-nounique.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64le-windows-gnu -emit-llvm -o - %s | FileCheck %s + +// An empty struct is handled as a struct with a dummy i8, on all targets. +// Most targets treat an empty struct return value as essentially void - but +// some don't. (Currently, at least x86_64-windows-* and powerpc64le-* don't +// treat it as void.) +// +// When intializing a struct with such a no_unique_address member, make sure we +// don't write the dummy i8 into the struct where there's no space allocated for +// it. +// +// This can only be tested with targets that don't treat empty struct returns as +// void. + +struct S {}; +S f(); +struct Z { + int x; + [[no_unique_address]] S y; + Z(); +}; +Z::Z() : x(111), y(f()) {} + +// CHECK: define {{.*}} @_ZN1ZC2Ev +// CHECK: %call = call i8 @_Z1fv() +// CHECK-NEXT: ret void + +struct S2 { + S2(); +}; +struct Z2 { + int x; + [[no_unique_address]] S2 y; + Z2(); +}; +Z2::Z2() : x(111) {} + +// CHECK: define {{.*}} @_ZN2Z2C2Ev(ptr {{.*}} %this) +// CHECK: %this.addr = alloca ptr +// CHECK: store ptr %this, ptr %this.addr +// CHECK: %this1 = load ptr, ptr %this.addr +// CHECK: call void @_ZN2S2C1Ev(ptr {{.*}} %this1)