[clang] 854f5f3 - [Sema] Teach -Wcast-align to compute an accurate alignment using the
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Fri May 15 01:02:23 PDT 2020
Author: Akira Hatanaka
Date: 2020-05-15T00:59:03-07:00
New Revision: 854f5f332af4640d9425e9a94442629e4f5a3f98
URL: https://github.com/llvm/llvm-project/commit/854f5f332af4640d9425e9a94442629e4f5a3f98
DIFF: https://github.com/llvm/llvm-project/commit/854f5f332af4640d9425e9a94442629e4f5a3f98.diff
LOG: [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
Added:
Modified:
clang/lib/Sema/SemaChecking.cpp
clang/test/SemaCXX/warn-cast-align.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b4cb76d0b1ad..fbf0fb036fe0 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<ParmVarDecl *> 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<DeclRefExpr>(E))
- return Context.getDeclAlign(DRE->getDecl());
+Optional<std::pair<CharUnits, CharUnits>>
+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<CharUnits, CharUnits>
+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<std::pair<CharUnits, CharUnits>>
+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<MemberExpr>(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<std::pair<CharUnits, CharUnits>>
+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<CastExpr>(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<ArraySubscriptExpr>(E);
+ return getAlignmentAndOffsetFromBinAddOrSub(ASE->getBase(), ASE->getIdx(),
+ false, Ctx);
+ }
+ case Stmt::DeclRefExprClass: {
+ if (auto *VD = dyn_cast<VarDecl>(cast<DeclRefExpr>(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<MemberExpr>(E);
+ if (ME->isArrow())
+ break;
+ auto *FD = dyn_cast<FieldDecl>(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<UnaryOperator>(E);
+ switch (UO->getOpcode()) {
+ default:
+ break;
+ case UO_Deref:
+ return getBaseAlignmentAndOffsetFromPtr(UO->getSubExpr(), Ctx);
+ }
+ break;
+ }
+ case Stmt::BinaryOperatorClass: {
+ auto *BO = cast<BinaryOperator>(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<std::pair<CharUnits, CharUnits>>
+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<CastExpr>(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<UnaryOperator>(E);
+ if (UO->getOpcode() == UO_AddrOf)
+ return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx);
+ break;
+ }
+ case Stmt::BinaryOperatorClass: {
+ auto *BO = cast<BinaryOperator>(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<std::pair<CharUnits, CharUnits>> 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<CastExpr>(Op)) {
- if (CE->getCastKind() == CK_ArrayToPointerDecay)
- SrcAlign = getDeclAlign(CE->getSubExpr(), SrcAlign, Context);
- } else if (auto *UO = dyn_cast<UnaryOperator>(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 68acbdd4eaa3..53cd75fb1405 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<B1 *>(&t5));
+ a = (A *)(&(static_cast<B1 &>(t5)));
+ a = (A *)(static_cast<B0 *>(&t6)); // expected-warning {{cast from 'B0 *' to 'A *'}}
+ a = (A *)(static_cast<B2 *>(&t7)); // expected-warning {{cast from 'B2 *' to 'A *'}}
+ a = (A *)(static_cast<B3 *>(&t7));
+ a = (A *)(static_cast<B2 *>(&t8)); // expected-warning {{cast from 'B2 *' to 'A *'}}
+ a = (A *)(static_cast<B3 *>(&t8)); // expected-warning {{cast from 'B3 *' to 'A *'}}
+ a = (A *)(static_cast<D5 *>(&t9)); // expected-warning {{cast from 'D5 *' to 'A *'}}
+ a = (A *)(static_cast<D3 *>(&t10)); // expected-warning {{cast from 'D3 *' to 'A *'}}
+}
More information about the cfe-commits
mailing list