From 854f5f332af4640d9425e9a94442629e4f5a3f98 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Sun, 22 Mar 2020 23:47:01 -0700 Subject: [PATCH] [Sema] Teach -Wcast-align to compute an accurate alignment using the alignment information on VarDecls in more cases This commit improves upon https://reviews.llvm.org/D21099. The code that computes the source alignment now understands array subscript expressions, binary operators, derived-to-base casts, and several more expressions. rdar://problem/59242343 Differential Revision: https://reviews.llvm.org/D78767 --- clang/lib/Sema/SemaChecking.cpp | 238 ++++++++++++++++++++++++++++++--- clang/test/SemaCXX/warn-cast-align.cpp | 114 +++++++++++++++- 2 files changed, 333 insertions(+), 19 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index b4cb76d..fbf0fb0 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -30,6 +30,7 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/RecordLayout.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" @@ -13105,17 +13106,226 @@ bool Sema::CheckParmsForFunctionDef(ArrayRef Parameters, return HasInvalidParm; } -/// A helper function to get the alignment of a Decl referred to by DeclRefExpr -/// or MemberExpr. -static CharUnits getDeclAlign(Expr *E, CharUnits TypeAlign, - ASTContext &Context) { - if (const auto *DRE = dyn_cast(E)) - return Context.getDeclAlign(DRE->getDecl()); +Optional> +static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx); + +/// Compute the alignment and offset of the base class object given the +/// derived-to-base cast expression and the alignment and offset of the derived +/// class object. +static std::pair +getDerivedToBaseAlignmentAndOffset(const CastExpr *CE, QualType DerivedType, + CharUnits BaseAlignment, CharUnits Offset, + ASTContext &Ctx) { + for (auto PathI = CE->path_begin(), PathE = CE->path_end(); PathI != PathE; + ++PathI) { + const CXXBaseSpecifier *Base = *PathI; + const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); + if (Base->isVirtual()) { + // The complete object may have a lower alignment than the non-virtual + // alignment of the base, in which case the base may be misaligned. Choose + // the smaller of the non-virtual alignment and BaseAlignment, which is a + // conservative lower bound of the complete object alignment. + CharUnits NonVirtualAlignment = + Ctx.getASTRecordLayout(BaseDecl).getNonVirtualAlignment(); + BaseAlignment = std::min(BaseAlignment, NonVirtualAlignment); + Offset = CharUnits::Zero(); + } else { + const ASTRecordLayout &RL = + Ctx.getASTRecordLayout(DerivedType->getAsCXXRecordDecl()); + Offset += RL.getBaseClassOffset(BaseDecl); + } + DerivedType = Base->getType(); + } + + return std::make_pair(BaseAlignment, Offset); +} + +/// Compute the alignment and offset of a binary additive operator. +static Optional> +getAlignmentAndOffsetFromBinAddOrSub(const Expr *PtrE, const Expr *IntE, + bool IsSub, ASTContext &Ctx) { + QualType PointeeType = PtrE->getType()->getPointeeType(); + + if (!PointeeType->isConstantSizeType()) + return llvm::None; + + auto P = getBaseAlignmentAndOffsetFromPtr(PtrE, Ctx); + + if (!P) + return llvm::None; - if (const auto *ME = dyn_cast(E)) - return Context.getDeclAlign(ME->getMemberDecl()); + llvm::APSInt IdxRes; + CharUnits EltSize = Ctx.getTypeSizeInChars(PointeeType); + if (IntE->isIntegerConstantExpr(IdxRes, Ctx)) { + CharUnits Offset = EltSize * IdxRes.getExtValue(); + if (IsSub) + Offset = -Offset; + return std::make_pair(P->first, P->second + Offset); + } - return TypeAlign; + // If the integer expression isn't a constant expression, compute the lower + // bound of the alignment using the alignment and offset of the pointer + // expression and the element size. + return std::make_pair( + P->first.alignmentAtOffset(P->second).alignmentAtOffset(EltSize), + CharUnits::Zero()); +} + +/// This helper function takes an lvalue expression and returns the alignment of +/// a VarDecl and a constant offset from the VarDecl. +Optional> +static getBaseAlignmentAndOffsetFromLValue(const Expr *E, ASTContext &Ctx) { + E = E->IgnoreParens(); + switch (E->getStmtClass()) { + default: + break; + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::ImplicitCastExprClass: { + auto *CE = cast(E); + const Expr *From = CE->getSubExpr(); + switch (CE->getCastKind()) { + default: + break; + case CK_NoOp: + return getBaseAlignmentAndOffsetFromLValue(From, Ctx); + case CK_UncheckedDerivedToBase: + case CK_DerivedToBase: { + auto P = getBaseAlignmentAndOffsetFromLValue(From, Ctx); + if (!P) + break; + return getDerivedToBaseAlignmentAndOffset(CE, From->getType(), P->first, + P->second, Ctx); + } + } + break; + } + case Stmt::ArraySubscriptExprClass: { + auto *ASE = cast(E); + return getAlignmentAndOffsetFromBinAddOrSub(ASE->getBase(), ASE->getIdx(), + false, Ctx); + } + case Stmt::DeclRefExprClass: { + if (auto *VD = dyn_cast(cast(E)->getDecl())) { + // FIXME: If VD is captured by copy or is an escaping __block variable, + // use the alignment of VD's type. + if (!VD->getType()->isReferenceType()) + return std::make_pair(Ctx.getDeclAlign(VD), CharUnits::Zero()); + if (VD->hasInit()) + return getBaseAlignmentAndOffsetFromLValue(VD->getInit(), Ctx); + } + break; + } + case Stmt::MemberExprClass: { + auto *ME = cast(E); + if (ME->isArrow()) + break; + auto *FD = dyn_cast(ME->getMemberDecl()); + if (!FD || FD->getType()->isReferenceType()) + break; + auto P = getBaseAlignmentAndOffsetFromLValue(ME->getBase(), Ctx); + if (!P) + break; + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(FD->getParent()); + uint64_t Offset = Layout.getFieldOffset(FD->getFieldIndex()); + return std::make_pair(P->first, + P->second + CharUnits::fromQuantity(Offset)); + } + case Stmt::UnaryOperatorClass: { + auto *UO = cast(E); + switch (UO->getOpcode()) { + default: + break; + case UO_Deref: + return getBaseAlignmentAndOffsetFromPtr(UO->getSubExpr(), Ctx); + } + break; + } + case Stmt::BinaryOperatorClass: { + auto *BO = cast(E); + auto Opcode = BO->getOpcode(); + switch (Opcode) { + default: + break; + case BO_Comma: + return getBaseAlignmentAndOffsetFromLValue(BO->getRHS(), Ctx); + } + break; + } + } + return llvm::None; +} + +/// This helper function takes a pointer expression and returns the alignment of +/// a VarDecl and a constant offset from the VarDecl. +Optional> +static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx) { + E = E->IgnoreParens(); + switch (E->getStmtClass()) { + default: + break; + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::ImplicitCastExprClass: { + auto *CE = cast(E); + const Expr *From = CE->getSubExpr(); + switch (CE->getCastKind()) { + default: + break; + case CK_NoOp: + return getBaseAlignmentAndOffsetFromPtr(From, Ctx); + case CK_ArrayToPointerDecay: + return getBaseAlignmentAndOffsetFromLValue(From, Ctx); + case CK_UncheckedDerivedToBase: + case CK_DerivedToBase: { + auto P = getBaseAlignmentAndOffsetFromPtr(From, Ctx); + if (!P) + break; + return getDerivedToBaseAlignmentAndOffset( + CE, From->getType()->getPointeeType(), P->first, P->second, Ctx); + } + } + break; + } + case Stmt::UnaryOperatorClass: { + auto *UO = cast(E); + if (UO->getOpcode() == UO_AddrOf) + return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx); + break; + } + case Stmt::BinaryOperatorClass: { + auto *BO = cast(E); + auto Opcode = BO->getOpcode(); + switch (Opcode) { + default: + break; + case BO_Add: + case BO_Sub: { + const Expr *LHS = BO->getLHS(), *RHS = BO->getRHS(); + if (Opcode == BO_Add && !RHS->getType()->isIntegralOrEnumerationType()) + std::swap(LHS, RHS); + return getAlignmentAndOffsetFromBinAddOrSub(LHS, RHS, Opcode == BO_Sub, + Ctx); + } + case BO_Comma: + return getBaseAlignmentAndOffsetFromPtr(BO->getRHS(), Ctx); + } + break; + } + } + return llvm::None; +} + +static CharUnits getPresumedAlignmentOfPointer(const Expr *E, Sema &S) { + // See if we can compute the alignment of a VarDecl and an offset from it. + Optional> P = + getBaseAlignmentAndOffsetFromPtr(E, S.Context); + + if (P) + return P->first.alignmentAtOffset(P->second); + + // If that failed, return the type's alignment. + return S.Context.getTypeAlignInChars(E->getType()->getPointeeType()); } /// CheckCastAlign - Implements -Wcast-align, which warns when a @@ -13151,15 +13361,7 @@ void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) { // includes 'void'. if (SrcPointee->isIncompleteType()) return; - CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee); - - if (auto *CE = dyn_cast(Op)) { - if (CE->getCastKind() == CK_ArrayToPointerDecay) - SrcAlign = getDeclAlign(CE->getSubExpr(), SrcAlign, Context); - } else if (auto *UO = dyn_cast(Op)) { - if (UO->getOpcode() == UO_AddrOf) - SrcAlign = getDeclAlign(UO->getSubExpr(), SrcAlign, Context); - } + CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this); if (SrcAlign >= DestAlign) return; diff --git a/clang/test/SemaCXX/warn-cast-align.cpp b/clang/test/SemaCXX/warn-cast-align.cpp index 68acbdd4..53cd75f 100644 --- a/clang/test/SemaCXX/warn-cast-align.cpp +++ b/clang/test/SemaCXX/warn-cast-align.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -Wcast-align -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -Wno-unused-value -Wcast-align -verify %s // Simple casts. void test0(char *P) { @@ -43,3 +43,115 @@ void test1(void *P) { typedef int *IntPtr; c = IntPtr(P); } + +struct __attribute__((aligned(16))) A { + char m0[16]; + char m1[16]; +}; + +struct B0 { + char m0[16]; +}; + +struct B1 { + char m0[16]; +}; + +struct C { + A &m0; + B0 &m1; + A m2; +}; + +struct __attribute__((aligned(16))) D0 : B0, B1 { +}; + +struct __attribute__((aligned(16))) D1 : virtual B0 { +}; + +struct B2 { + char m0[8]; +}; + +struct B3 { + char m0[8]; +}; + +struct B4 { + char m0[8]; +}; + +struct D2 : B2, B3 { +}; + +struct __attribute__((aligned(16))) D3 : B4, D2 { +}; + +struct __attribute__((aligned(16))) D4 : virtual D2 { +}; + +struct D5 : virtual D0 { + char m0[16]; +}; + +struct D6 : virtual D5 { +}; + +struct D7 : virtual D3 { +}; + +void test2(int n, A *a2) { + __attribute__((aligned(16))) char m[sizeof(A) * 2]; + char(&m_ref)[sizeof(A) * 2] = m; + extern char(&m_ref_noinit)[sizeof(A) * 2]; + __attribute__((aligned(16))) char vararray[10][n]; + A t0; + B0 t1; + C t2 = {.m0 = t0, .m1 = t1}; + __attribute__((aligned(16))) char t3[5][5][5]; + __attribute__((aligned(16))) char t4[4][16]; + D0 t5; + D1 t6; + D3 t7; + D4 t8; + D6 t9; + __attribute__((aligned(1))) D7 t10; + + A *a; + a = (A *)&m; + a = (A *)(m + sizeof(A)); + a = (A *)(sizeof(A) + m); + a = (A *)((sizeof(A) * 2 + m) - sizeof(A)); + a = (A *)((sizeof(A) * 2 + m) - 1); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(m + 1); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(1 + m); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(m + n); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)&*&m[sizeof(A)]; + a = (A *)(0, 0, &m[sizeof(A)]); + a = (A *)&(0, 0, *&m[sizeof(A)]); + a = (A *)&m[n]; // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)&m_ref; + a = (A *)&m_ref_noinit; // expected-warning {{cast from 'char (*)[64]' to 'A *'}} + a = (A *)(&vararray[4][0]); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(a2->m0 + sizeof(A)); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(&t2.m0); + a = (A *)(&t2.m1); // expected-warning {{cast from 'B0 *' to 'A *'}} + a = (A *)(&t2.m2); + a = (A *)(t2.m2.m1); + a = (A *)(&t3[3][3][0]); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(&t3[2][2][4]); + a = (A *)(&t3[0][n][0]); // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)&t4[n][0]; + a = (A *)&t4[n][1]; // expected-warning {{cast from 'char *' to 'A *'}} + a = (A *)(t4 + 1); + a = (A *)(t4 + n); + a = (A *)(static_cast(&t5)); + a = (A *)(&(static_cast(t5))); + a = (A *)(static_cast(&t6)); // expected-warning {{cast from 'B0 *' to 'A *'}} + a = (A *)(static_cast(&t7)); // expected-warning {{cast from 'B2 *' to 'A *'}} + a = (A *)(static_cast(&t7)); + a = (A *)(static_cast(&t8)); // expected-warning {{cast from 'B2 *' to 'A *'}} + a = (A *)(static_cast(&t8)); // expected-warning {{cast from 'B3 *' to 'A *'}} + a = (A *)(static_cast(&t9)); // expected-warning {{cast from 'D5 *' to 'A *'}} + a = (A *)(static_cast(&t10)); // expected-warning {{cast from 'D3 *' to 'A *'}} +} -- 2.7.4