From: Akira Hatanaka Date: Tue, 27 Jun 2017 04:34:04 +0000 (+0000) Subject: [CodeGen][ObjC] Fix GNU's encoding of bit-field ivars. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=11150dad8c242355c230f1a2da0ac37b7df59ffa;p=clang [CodeGen][ObjC] Fix GNU's encoding of bit-field ivars. According to the documentation, when encoding a bit-field, GNU runtime needs its starting position in addition to its type and size. https://gcc.gnu.org/onlinedocs/gcc/Type-encoding.html Prior to r297702, the starting position information was not being encoded, which is incorrect, and after r297702, an assertion started to fail because an ObjCIvarDecl was being passed to a function expecting a FieldDecl. This commit moves LookupFieldBitOffset to ASTContext and uses the function to encode the starting position of bit-fields. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@306364 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 4c379620ab..3b46d31458 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -2050,6 +2050,11 @@ public: /// Get the offset of a FieldDecl or IndirectFieldDecl, in bits. uint64_t getFieldOffset(const ValueDecl *FD) const; + /// Get the offset of an ObjCIvarDecl in bits. + uint64_t lookupFieldBitOffset(const ObjCInterfaceDecl *OID, + const ObjCImplementationDecl *ID, + const ObjCIvarDecl *Ivar) const; + bool isNearlyEmpty(const CXXRecordDecl *RD) const; VTableContextBase *getVTableContext(); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fabfdc9ef7..a2ff176df1 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -5990,9 +5990,19 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S, // compatibility with GCC, although providing it breaks anything that // actually uses runtime introspection and wants to work on both runtimes... if (Ctx->getLangOpts().ObjCRuntime.isGNUFamily()) { - const RecordDecl *RD = FD->getParent(); - const ASTRecordLayout &RL = Ctx->getASTRecordLayout(RD); - S += llvm::utostr(RL.getFieldOffset(FD->getFieldIndex())); + uint64_t Offset; + + if (const auto *IVD = dyn_cast(FD)) { + Offset = Ctx->lookupFieldBitOffset(IVD->getContainingInterface(), nullptr, + IVD); + } else { + const RecordDecl *RD = FD->getParent(); + const ASTRecordLayout &RL = Ctx->getASTRecordLayout(RD); + Offset = RL.getFieldOffset(FD->getFieldIndex()); + } + + S += llvm::utostr(Offset); + if (const EnumType *ET = T->getAs()) S += ObjCEncodingForEnumType(Ctx, ET); else { diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index cf981be0a4..c0b9cadca4 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -3073,6 +3073,41 @@ uint64_t ASTContext::getFieldOffset(const ValueDecl *VD) const { return OffsetInBits; } +uint64_t ASTContext::lookupFieldBitOffset(const ObjCInterfaceDecl *OID, + const ObjCImplementationDecl *ID, + const ObjCIvarDecl *Ivar) const { + const ObjCInterfaceDecl *Container = Ivar->getContainingInterface(); + + // FIXME: We should eliminate the need to have ObjCImplementationDecl passed + // in here; it should never be necessary because that should be the lexical + // decl context for the ivar. + + // If we know have an implementation (and the ivar is in it) then + // look up in the implementation layout. + const ASTRecordLayout *RL; + if (ID && declaresSameEntity(ID->getClassInterface(), Container)) + RL = &getASTObjCImplementationLayout(ID); + else + RL = &getASTObjCInterfaceLayout(Container); + + // Compute field index. + // + // FIXME: The index here is closely tied to how ASTContext::getObjCLayout is + // implemented. This should be fixed to get the information from the layout + // directly. + unsigned Index = 0; + + for (const ObjCIvarDecl *IVD = Container->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + if (Ivar == IVD) + break; + ++Index; + } + assert(Index < RL->getFieldCount() && "Ivar is not inside record layout!"); + + return RL->getFieldOffset(Index); +} + /// getObjCLayout - Get or compute information about the layout of the /// given interface. /// diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp index b5599dad30..4cfddcb107 100644 --- a/lib/CodeGen/CGObjCRuntime.cpp +++ b/lib/CodeGen/CGObjCRuntime.cpp @@ -26,61 +26,27 @@ using namespace clang; using namespace CodeGen; -static uint64_t LookupFieldBitOffset(CodeGen::CodeGenModule &CGM, - const ObjCInterfaceDecl *OID, - const ObjCImplementationDecl *ID, - const ObjCIvarDecl *Ivar) { - const ObjCInterfaceDecl *Container = Ivar->getContainingInterface(); - - // FIXME: We should eliminate the need to have ObjCImplementationDecl passed - // in here; it should never be necessary because that should be the lexical - // decl context for the ivar. - - // If we know have an implementation (and the ivar is in it) then - // look up in the implementation layout. - const ASTRecordLayout *RL; - if (ID && declaresSameEntity(ID->getClassInterface(), Container)) - RL = &CGM.getContext().getASTObjCImplementationLayout(ID); - else - RL = &CGM.getContext().getASTObjCInterfaceLayout(Container); - - // Compute field index. - // - // FIXME: The index here is closely tied to how ASTContext::getObjCLayout is - // implemented. This should be fixed to get the information from the layout - // directly. - unsigned Index = 0; - - for (const ObjCIvarDecl *IVD = Container->all_declared_ivar_begin(); - IVD; IVD = IVD->getNextIvar()) { - if (Ivar == IVD) - break; - ++Index; - } - assert(Index < RL->getFieldCount() && "Ivar is not inside record layout!"); - - return RL->getFieldOffset(Index); -} - uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM, const ObjCInterfaceDecl *OID, const ObjCIvarDecl *Ivar) { - return LookupFieldBitOffset(CGM, OID, nullptr, Ivar) / - CGM.getContext().getCharWidth(); + return CGM.getContext().lookupFieldBitOffset(OID, nullptr, Ivar) / + CGM.getContext().getCharWidth(); } uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM, const ObjCImplementationDecl *OID, const ObjCIvarDecl *Ivar) { - return LookupFieldBitOffset(CGM, OID->getClassInterface(), OID, Ivar) / - CGM.getContext().getCharWidth(); + return CGM.getContext().lookupFieldBitOffset(OID->getClassInterface(), OID, + Ivar) / + CGM.getContext().getCharWidth(); } unsigned CGObjCRuntime::ComputeBitfieldBitOffset( CodeGen::CodeGenModule &CGM, const ObjCInterfaceDecl *ID, const ObjCIvarDecl *Ivar) { - return LookupFieldBitOffset(CGM, ID, ID->getImplementation(), Ivar); + return CGM.getContext().lookupFieldBitOffset(ID, ID->getImplementation(), + Ivar); } LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, @@ -119,7 +85,8 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, // Note, there is a subtle invariant here: we can only call this routine on // non-synthesized ivars but we may be called for synthesized ivars. However, // a synthesized ivar can never be a bit-field, so this is safe. - uint64_t FieldBitOffset = LookupFieldBitOffset(CGF.CGM, OID, nullptr, Ivar); + uint64_t FieldBitOffset = + CGF.CGM.getContext().lookupFieldBitOffset(OID, nullptr, Ivar); uint64_t BitOffset = FieldBitOffset % CGF.CGM.getContext().getCharWidth(); uint64_t AlignmentBits = CGF.CGM.getTarget().getCharAlign(); uint64_t BitFieldSize = Ivar->getBitWidthValue(CGF.getContext()); diff --git a/test/CodeGenObjC/ivar-type-encoding.m b/test/CodeGenObjC/ivar-type-encoding.m index ffa5e0d7af..d3afdc5f11 100644 --- a/test/CodeGenObjC/ivar-type-encoding.m +++ b/test/CodeGenObjC/ivar-type-encoding.m @@ -33,3 +33,19 @@ int main() { // CHECK: @1 = private unnamed_addr constant [12 x i8] c"@\22NSString\22\00" // CHECK: @2 = private unnamed_addr constant [9 x i8] c"_intIvar\00" // CHECK: @3 = private unnamed_addr constant [2 x i8] c"i\00" + +@interface Class1 { + int : 3; + short : 2; + long long ll; + char : 1; +} +@end + +@implementation Class1 +@end + +// CHECK: @{{.*}} = private unnamed_addr constant [5 x i8] c"b0i3\00" +// CHECK: @{{.*}} = private unnamed_addr constant [5 x i8] c"b3s2\00" +// CHECK: @{{.*}} = private unnamed_addr constant [2 x i8] c"q\00" +// CHECK: @{{.*}} = private unnamed_addr constant [7 x i8] c"b128c1\00"