[ObjC++] Never pass structs that transitively contain __weak fields in

registers.

This patch fixes a bug in r328731 that caused structs transitively
containing __weak fields to be passed in registers. The patch replaces
the flag RecordDecl::CanPassInRegisters with a 2-bit enum that indicates
whether the struct or structs containing the struct are forced to be
passed indirectly.

This reapplies r329617. r329617 didn't specify the underlying type for
enum ArgPassingKind, which caused regression tests to fail on a windows
bot.

rdar://problem/39194693

Differential Revision: https://reviews.llvm.org/D45384

llvm-svn: 329635
This commit is contained in:
Akira Hatanaka 2018-04-09 22:48:22 +00:00
parent 3539c09d3b
commit e6313ace66
11 changed files with 102 additions and 47 deletions

View File

@ -3541,6 +3541,31 @@ public:
/// union Y { int A, B; }; // Has body with members A and B (FieldDecls). /// union Y { int A, B; }; // Has body with members A and B (FieldDecls).
/// This decl will be marked invalid if *any* members are invalid. /// This decl will be marked invalid if *any* members are invalid.
class RecordDecl : public TagDecl { class RecordDecl : public TagDecl {
public:
/// Enum that represents the different ways arguments are passed to and
/// returned from function calls. This takes into account the target-specific
/// and version-specific rules along with the rules determined by the
/// language.
enum ArgPassingKind : unsigned {
/// The argument of this type can be passed directly in registers.
APK_CanPassInRegs,
/// The argument of this type cannot be passed directly in registers.
/// Records containing this type as a subobject are not forced to be passed
/// indirectly. This value is used only in C++. This value is required by
/// C++ because, in uncommon situations, it is possible for a class to have
/// only trivial copy/move constructors even when one of its subobjects has
/// a non-trivial copy/move constructor (if e.g. the corresponding copy/move
/// constructor in the derived class is deleted).
APK_CannotPassInRegs,
/// The argument of this type cannot be passed directly in registers.
/// Records containing this type as a subobject are forced to be passed
/// indirectly.
APK_CanNeverPassInRegs
};
private:
friend class DeclContext; friend class DeclContext;
// FIXME: This can be packed into the bitfields in Decl. // FIXME: This can be packed into the bitfields in Decl.
@ -3571,17 +3596,14 @@ class RecordDecl : public TagDecl {
bool NonTrivialToPrimitiveCopy : 1; bool NonTrivialToPrimitiveCopy : 1;
bool NonTrivialToPrimitiveDestroy : 1; bool NonTrivialToPrimitiveDestroy : 1;
/// True if this class can be passed in a non-address-preserving fashion
/// (such as in registers).
/// This does not imply anything about how the ABI in use will actually
/// pass an object of this class.
bool CanPassInRegisters : 1;
/// Indicates whether this struct is destroyed in the callee. This flag is /// Indicates whether this struct is destroyed in the callee. This flag is
/// meaningless when Microsoft ABI is used since parameters are always /// meaningless when Microsoft ABI is used since parameters are always
/// destroyed in the callee. /// destroyed in the callee.
bool ParamDestroyedInCallee : 1; bool ParamDestroyedInCallee : 1;
/// Represents the way this type is passed to a function.
ArgPassingKind ArgPassingRestrictions : 2;
protected: protected:
RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC, RecordDecl(Kind DK, TagKind TK, const ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, SourceLocation IdLoc, SourceLocation StartLoc, SourceLocation IdLoc,
@ -3669,12 +3691,15 @@ public:
/// it must have at least one trivial, non-deleted copy or move constructor. /// it must have at least one trivial, non-deleted copy or move constructor.
/// FIXME: This should be set as part of completeDefinition. /// FIXME: This should be set as part of completeDefinition.
bool canPassInRegisters() const { bool canPassInRegisters() const {
return CanPassInRegisters; return ArgPassingRestrictions == APK_CanPassInRegs;
} }
/// Set that we can pass this RecordDecl in registers. ArgPassingKind getArgPassingRestrictions() const {
void setCanPassInRegisters(bool CanPass) { return ArgPassingRestrictions;
CanPassInRegisters = CanPass; }
void setArgPassingRestrictions(ArgPassingKind Kind) {
ArgPassingRestrictions = Kind;
} }
bool isParamDestroyedInCallee() const { bool isParamDestroyedInCallee() const {

View File

@ -1149,8 +1149,6 @@ public:
/// source object is placed in an uninitialized state. /// source object is placed in an uninitialized state.
PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const; PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const;
bool canPassInRegisters() const;
enum DestructionKind { enum DestructionKind {
DK_none, DK_none,
DK_cxx_destructor, DK_cxx_destructor,

View File

@ -3956,7 +3956,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
LoadedFieldsFromExternalStorage(false), LoadedFieldsFromExternalStorage(false),
NonTrivialToPrimitiveDefaultInitialize(false), NonTrivialToPrimitiveDefaultInitialize(false),
NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false), NonTrivialToPrimitiveCopy(false), NonTrivialToPrimitiveDestroy(false),
CanPassInRegisters(true), ParamDestroyedInCallee(false) { ParamDestroyedInCallee(false), ArgPassingRestrictions(APK_CanPassInRegs) {
assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!"); assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
} }

View File

@ -421,6 +421,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
if (BaseClassDecl->hasVolatileMember()) if (BaseClassDecl->hasVolatileMember())
setHasVolatileMember(true); setHasVolatileMember(true);
if (BaseClassDecl->getArgPassingRestrictions() ==
RecordDecl::APK_CanNeverPassInRegs)
setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
// Keep track of the presence of mutable fields. // Keep track of the presence of mutable fields.
if (BaseClassDecl->hasMutableFields()) { if (BaseClassDecl->hasMutableFields()) {
data().HasMutableFields = true; data().HasMutableFields = true;
@ -950,7 +954,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
// Structs with __weak fields should never be passed directly. // Structs with __weak fields should never be passed directly.
if (LT == Qualifiers::OCL_Weak) if (LT == Qualifiers::OCL_Weak)
setCanPassInRegisters(false); setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
Data.HasIrrelevantDestructor = false; Data.HasIrrelevantDestructor = false;
} else if (!Context.getLangOpts().ObjCAutoRefCount) { } else if (!Context.getLangOpts().ObjCAutoRefCount) {
@ -1117,6 +1121,9 @@ void CXXRecordDecl::addedMember(Decl *D) {
setHasObjectMember(true); setHasObjectMember(true);
if (FieldRec->hasVolatileMember()) if (FieldRec->hasVolatileMember())
setHasVolatileMember(true); setHasVolatileMember(true);
if (FieldRec->getArgPassingRestrictions() ==
RecordDecl::APK_CanNeverPassInRegs)
setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
// C++0x [class]p7: // C++0x [class]p7:
// A standard-layout class is a class that: // A standard-layout class is a class that:

View File

@ -2239,17 +2239,6 @@ QualType::isNonTrivialToPrimitiveDestructiveMove() const {
return isNonTrivialToPrimitiveCopy(); return isNonTrivialToPrimitiveCopy();
} }
bool QualType::canPassInRegisters() const {
if (const auto *RT =
getTypePtr()->getBaseElementTypeUnsafe()->getAs<RecordType>())
return RT->getDecl()->canPassInRegisters();
if (getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak)
return false;
return true;
}
bool Type::isLiteralType(const ASTContext &Ctx) const { bool Type::isLiteralType(const ASTContext &Ctx) const {
if (isDependentType()) if (isDependentType())
return false; return false;

View File

@ -15465,8 +15465,13 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
Record->setNonTrivialToPrimitiveDestroy(true); Record->setNonTrivialToPrimitiveDestroy(true);
Record->setParamDestroyedInCallee(true); Record->setParamDestroyedInCallee(true);
} }
if (!FT.canPassInRegisters())
Record->setCanPassInRegisters(false); if (const auto *RT = FT->getAs<RecordType>()) {
if (RT->getDecl()->getArgPassingRestrictions() ==
RecordDecl::APK_CanNeverPassInRegs)
Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
} else if (FT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak)
Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs);
} }
if (Record && FD->getType().isVolatileQualified()) if (Record && FD->getType().isVolatileQualified())

View File

@ -5847,20 +5847,20 @@ static bool paramCanBeDestroyedInCallee(Sema &S, CXXRecordDecl *D,
return HasNonDeletedCopyOrMove; return HasNonDeletedCopyOrMove;
} }
static bool computeCanPassInRegister(bool DestroyedInCallee, static RecordDecl::ArgPassingKind
const CXXRecordDecl *RD, computeArgPassingRestrictions(bool DestroyedInCallee, const CXXRecordDecl *RD,
TargetInfo::CallingConvKind CCK, TargetInfo::CallingConvKind CCK, Sema &S) {
Sema &S) {
if (RD->isDependentType() || RD->isInvalidDecl()) if (RD->isDependentType() || RD->isInvalidDecl())
return true; return RecordDecl::APK_CanPassInRegs;
// The param cannot be passed in registers if CanPassInRegisters is already // The param cannot be passed in registers if ArgPassingRestrictions is set to
// set to false. // APK_CanNeverPassInRegs.
if (!RD->canPassInRegisters()) if (RD->getArgPassingRestrictions() == RecordDecl::APK_CanNeverPassInRegs)
return false; return RecordDecl::APK_CanNeverPassInRegs;
if (CCK != TargetInfo::CCK_MicrosoftX86_64) if (CCK != TargetInfo::CCK_MicrosoftX86_64)
return DestroyedInCallee; return DestroyedInCallee ? RecordDecl::APK_CanPassInRegs
: RecordDecl::APK_CannotPassInRegs;
bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false; bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false;
bool DtorIsTrivialForCall = false; bool DtorIsTrivialForCall = false;
@ -5900,7 +5900,7 @@ static bool computeCanPassInRegister(bool DestroyedInCallee,
// If the copy ctor and dtor are both trivial-for-calls, pass direct. // If the copy ctor and dtor are both trivial-for-calls, pass direct.
if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall) if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall)
return true; return RecordDecl::APK_CanPassInRegs;
// If a class has a destructor, we'd really like to pass it indirectly // If a class has a destructor, we'd really like to pass it indirectly
// because it allows us to elide copies. Unfortunately, MSVC makes that // because it allows us to elide copies. Unfortunately, MSVC makes that
@ -5914,8 +5914,8 @@ static bool computeCanPassInRegister(bool DestroyedInCallee,
// passed in registers, which is non-conforming. // passed in registers, which is non-conforming.
if (CopyCtorIsTrivial && if (CopyCtorIsTrivial &&
S.getASTContext().getTypeSize(RD->getTypeForDecl()) <= 64) S.getASTContext().getTypeSize(RD->getTypeForDecl()) <= 64)
return true; return RecordDecl::APK_CanPassInRegs;
return false; return RecordDecl::APK_CannotPassInRegs;
} }
/// \brief Perform semantic checks on a class definition that has been /// \brief Perform semantic checks on a class definition that has been
@ -6090,8 +6090,8 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
if (Record->hasNonTrivialDestructor()) if (Record->hasNonTrivialDestructor())
Record->setParamDestroyedInCallee(DestroyedInCallee); Record->setParamDestroyedInCallee(DestroyedInCallee);
Record->setCanPassInRegisters( Record->setArgPassingRestrictions(
computeCanPassInRegister(DestroyedInCallee, Record, CCK, *this)); computeArgPassingRestrictions(DestroyedInCallee, Record, CCK, *this));
} }
/// Look up the special member function that would be called by a special /// Look up the special member function that would be called by a special

View File

@ -742,8 +742,8 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
RD->setNonTrivialToPrimitiveDefaultInitialize(Record.readInt()); RD->setNonTrivialToPrimitiveDefaultInitialize(Record.readInt());
RD->setNonTrivialToPrimitiveCopy(Record.readInt()); RD->setNonTrivialToPrimitiveCopy(Record.readInt());
RD->setNonTrivialToPrimitiveDestroy(Record.readInt()); RD->setNonTrivialToPrimitiveDestroy(Record.readInt());
RD->setCanPassInRegisters(Record.readInt());
RD->setParamDestroyedInCallee(Record.readInt()); RD->setParamDestroyedInCallee(Record.readInt());
RD->setArgPassingRestrictions((RecordDecl::ArgPassingKind)Record.readInt());
return Redecl; return Redecl;
} }
@ -4114,8 +4114,9 @@ void ASTDeclReader::UpdateDecl(Decl *D,
bool HadRealDefinition = bool HadRealDefinition =
OldDD && (OldDD->Definition != RD || OldDD && (OldDD->Definition != RD ||
!Reader.PendingFakeDefinitionData.count(OldDD)); !Reader.PendingFakeDefinitionData.count(OldDD));
RD->setCanPassInRegisters(Record.readInt());
RD->setParamDestroyedInCallee(Record.readInt()); RD->setParamDestroyedInCallee(Record.readInt());
RD->setArgPassingRestrictions(
(RecordDecl::ArgPassingKind)Record.readInt());
ReadCXXRecordDefinition(RD, /*Update*/true); ReadCXXRecordDefinition(RD, /*Update*/true);
// Visible update is handled separately. // Visible update is handled separately.

View File

@ -5193,8 +5193,8 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: { case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: {
auto *RD = cast<CXXRecordDecl>(D); auto *RD = cast<CXXRecordDecl>(D);
UpdatedDeclContexts.insert(RD->getPrimaryContext()); UpdatedDeclContexts.insert(RD->getPrimaryContext());
Record.push_back(RD->canPassInRegisters());
Record.push_back(RD->isParamDestroyedInCallee()); Record.push_back(RD->isParamDestroyedInCallee());
Record.push_back(RD->getArgPassingRestrictions());
Record.AddCXXDefinitionData(RD); Record.AddCXXDefinitionData(RD);
Record.AddOffset(WriteDeclContextLexicalBlock( Record.AddOffset(WriteDeclContextLexicalBlock(
*Context, const_cast<CXXRecordDecl *>(RD))); *Context, const_cast<CXXRecordDecl *>(RD)));

View File

@ -469,8 +469,8 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) {
Record.push_back(D->isNonTrivialToPrimitiveDefaultInitialize()); Record.push_back(D->isNonTrivialToPrimitiveDefaultInitialize());
Record.push_back(D->isNonTrivialToPrimitiveCopy()); Record.push_back(D->isNonTrivialToPrimitiveCopy());
Record.push_back(D->isNonTrivialToPrimitiveDestroy()); Record.push_back(D->isNonTrivialToPrimitiveDestroy());
Record.push_back(D->canPassInRegisters());
Record.push_back(D->isParamDestroyedInCallee()); Record.push_back(D->isParamDestroyedInCallee());
Record.push_back(D->getArgPassingRestrictions());
if (D->getDeclContext() == D->getLexicalDeclContext() && if (D->getDeclContext() == D->getLexicalDeclContext() &&
!D->hasAttrs() && !D->hasAttrs() &&
@ -1913,9 +1913,10 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
// isNonTrivialToPrimitiveDestroy // isNonTrivialToPrimitiveDestroy
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // canPassInRegisters
// isParamDestroyedInCallee // isParamDestroyedInCallee
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
// getArgPassingRestrictions
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2));
// DC // DC
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset

View File

@ -8,6 +8,8 @@
// pointer fields are passed directly. // pointer fields are passed directly.
// CHECK: %[[STRUCT_STRONGWEAK:.*]] = type { i8*, i8* } // CHECK: %[[STRUCT_STRONGWEAK:.*]] = type { i8*, i8* }
// CHECK: %[[STRUCT_CONTAINSSTRONGWEAK:.*]] = type { %[[STRUCT_STRONGWEAK]] }
// CHECK: %[[STRUCT_DERIVEDSTRONGWEAK:.*]] = type { %[[STRUCT_STRONGWEAK]] }
// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* } // CHECK: %[[STRUCT_STRONG:.*]] = type { i8* }
// CHECK: %[[STRUCT_S:.*]] = type { i8* } // CHECK: %[[STRUCT_S:.*]] = type { i8* }
// CHECK: %[[STRUCT_CONTAINSNONTRIVIAL:.*]] = type { %{{.*}}, i8* } // CHECK: %[[STRUCT_CONTAINSNONTRIVIAL:.*]] = type { %{{.*}}, i8* }
@ -21,6 +23,21 @@ struct StrongWeak {
__weak id fweak; __weak id fweak;
}; };
#ifdef TRIVIALABI
struct __attribute__((trivial_abi)) ContainsStrongWeak {
#else
struct ContainsStrongWeak {
#endif
StrongWeak sw;
};
#ifdef TRIVIALABI
struct __attribute__((trivial_abi)) DerivedStrongWeak : StrongWeak {
#else
struct DerivedStrongWeak : StrongWeak {
#endif
};
#ifdef TRIVIALABI #ifdef TRIVIALABI
struct __attribute__((trivial_abi)) Strong { struct __attribute__((trivial_abi)) Strong {
#else #else
@ -84,6 +101,18 @@ StrongWeak testReturnStrongWeak(StrongWeak *a) {
return *a; return *a;
} }
// CHECK: define void @_Z27testParamContainsStrongWeak18ContainsStrongWeak(%[[STRUCT_CONTAINSSTRONGWEAK]]* %[[A:.*]])
// CHECK: call %[[STRUCT_CONTAINSSTRONGWEAK]]* @_ZN18ContainsStrongWeakD1Ev(%[[STRUCT_CONTAINSSTRONGWEAK]]* %[[A]])
void testParamContainsStrongWeak(ContainsStrongWeak a) {
}
// CHECK: define void @_Z26testParamDerivedStrongWeak17DerivedStrongWeak(%[[STRUCT_DERIVEDSTRONGWEAK]]* %[[A:.*]])
// CHECK: call %[[STRUCT_DERIVEDSTRONGWEAK]]* @_ZN17DerivedStrongWeakD1Ev(%[[STRUCT_DERIVEDSTRONGWEAK]]* %[[A]])
void testParamDerivedStrongWeak(DerivedStrongWeak a) {
}
// CHECK: define void @_Z15testParamStrong6Strong(i64 %[[A_COERCE:.*]]) // CHECK: define void @_Z15testParamStrong6Strong(i64 %[[A_COERCE:.*]])
// CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG]], align 8 // CHECK: %[[A:.*]] = alloca %[[STRUCT_STRONG]], align 8
// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[A]], i32 0, i32 0 // CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[A]], i32 0, i32 0