[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