r218992 - constexpr evaluation for __builtin_assume_aligned

Hal Finkel hfinkel at anl.gov
Fri Oct 3 10:18:37 PDT 2014


Author: hfinkel
Date: Fri Oct  3 12:18:37 2014
New Revision: 218992

URL: http://llvm.org/viewvc/llvm-project?rev=218992&view=rev
Log:
constexpr evaluation for __builtin_assume_aligned

Richard noted in the review of r217349 that extra handling of
__builtin_assume_aligned inside of the expression evaluator was needed. He was
right, and this should address the concerns raised, namely:

 1. The offset argument to __builtin_assume_aligned can have side effects, and
    we need to make sure that all arguments are properly evaluated.

 2. If the alignment assumption does not hold, that introduces undefined
    behavior, and undefined behavior cannot appear inside a constexpr.

and hopefully the diagnostics produced are detailed enough to explain what is
going on.

Added:
    cfe/trunk/test/SemaCXX/builtin-assume-aligned.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
    cfe/trunk/lib/AST/ExprConstant.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=218992&r1=218991&r2=218992&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Fri Oct  3 12:18:37 2014
@@ -141,6 +141,13 @@ def note_constexpr_calls_suppressed : No
   "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
   "see all)">;
 def note_constexpr_call_here : Note<"in call to '%0'">;
+def note_constexpr_baa_insufficient_alignment : Note<
+  "%select{alignment of|offset of the aligned pointer from}0 the base pointee "
+  "object (%1 %plural{1:byte|:bytes}1) is %select{less than|not a multiple of}0 the "
+  "asserted %2 %plural{1:byte|:bytes}2">;
+def note_constexpr_baa_value_insufficient_alignment : Note<
+  "value of the aligned pointer (%0) is not a multiple of the asserted %1 "
+  "%plural{1:byte|:bytes}1">;
 
 def warn_integer_constant_overflow : Warning<
   "overflow in expression; result is %0 with type %1">,

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=218992&r1=218991&r2=218992&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Fri Oct  3 12:18:37 2014
@@ -4885,6 +4885,38 @@ bool PointerExprEvaluator::VisitCastExpr
   return ExprEvaluatorBaseTy::VisitCastExpr(E);
 }
 
+static CharUnits GetAlignOfType(EvalInfo &Info, QualType T) {
+  // C++ [expr.alignof]p3:
+  //     When alignof is applied to a reference type, the result is the
+  //     alignment of the referenced type.
+  if (const ReferenceType *Ref = T->getAs<ReferenceType>())
+    T = Ref->getPointeeType();
+
+  // __alignof is defined to return the preferred alignment.
+  return Info.Ctx.toCharUnitsFromBits(
+    Info.Ctx.getPreferredTypeAlign(T.getTypePtr()));
+}
+
+static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E) {
+  E = E->IgnoreParens();
+
+  // The kinds of expressions that we have special-case logic here for
+  // should be kept up to date with the special checks for those
+  // expressions in Sema.
+
+  // alignof decl is always accepted, even if it doesn't make sense: we default
+  // to 1 in those cases.
+  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+    return Info.Ctx.getDeclAlign(DRE->getDecl(),
+                                 /*RefAsPointee*/true);
+
+  if (const MemberExpr *ME = dyn_cast<MemberExpr>(E))
+    return Info.Ctx.getDeclAlign(ME->getMemberDecl(),
+                                 /*RefAsPointee*/true);
+
+  return GetAlignOfType(Info, E->getType());
+}
+
 bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) {
   if (IsStringLiteralCall(E))
     return Success(E);
@@ -4892,7 +4924,71 @@ bool PointerExprEvaluator::VisitCallExpr
   switch (E->getBuiltinCallee()) {
   case Builtin::BI__builtin_addressof:
     return EvaluateLValue(E->getArg(0), Result, Info);
+  case Builtin::BI__builtin_assume_aligned: {
+    // We need to be very careful here because: if the pointer does not have the
+    // asserted alignment, then the behavior is undefined, and undefined
+    // behavior is non-constant.
+    if (!EvaluatePointer(E->getArg(0), Result, Info))
+      return false;
+
+    LValue OffsetResult(Result);
+    APSInt Alignment;
+    if (!EvaluateInteger(E->getArg(1), Alignment, Info))
+      return false;
+    CharUnits Align = CharUnits::fromQuantity(getExtValue(Alignment));
+
+    if (E->getNumArgs() > 2) {
+      APSInt Offset;
+      if (!EvaluateInteger(E->getArg(2), Offset, Info))
+        return false;
+
+      int64_t AdditionalOffset = -getExtValue(Offset);
+      OffsetResult.Offset += CharUnits::fromQuantity(AdditionalOffset);
+    }
+
+    // If there is a base object, then it must have the correct alignment.
+    if (OffsetResult.Base) {
+      CharUnits BaseAlignment;
+      if (const ValueDecl *VD =
+          OffsetResult.Base.dyn_cast<const ValueDecl*>()) {
+        BaseAlignment = Info.Ctx.getDeclAlign(VD);
+      } else {
+        BaseAlignment =
+          GetAlignOfExpr(Info, OffsetResult.Base.get<const Expr*>());
+      }
+
+      if (BaseAlignment < Align) {
+        Result.Designator.setInvalid();
+	// FIXME: Quantities here cast to integers because the plural modifier
+	// does not work on APSInts yet.
+        CCEDiag(E->getArg(0),
+                diag::note_constexpr_baa_insufficient_alignment) << 0
+          << (int) BaseAlignment.getQuantity()
+          << (unsigned) getExtValue(Alignment);
+        return false;
+      }
+    }
+
+    // The offset must also have the correct alignment.
+    if (OffsetResult.Offset.RoundUpToAlignment(Align) != OffsetResult.Offset) {
+      Result.Designator.setInvalid();
+      APSInt Offset(64, false);
+      Offset = OffsetResult.Offset.getQuantity();
+
+      if (OffsetResult.Base)
+        CCEDiag(E->getArg(0),
+                diag::note_constexpr_baa_insufficient_alignment) << 1
+          << (int) getExtValue(Offset) << (unsigned) getExtValue(Alignment);
+      else
+        CCEDiag(E->getArg(0),
+                diag::note_constexpr_baa_value_insufficient_alignment)
+          << Offset << (unsigned) getExtValue(Alignment);
+
+      return false;
+    }
 
+    return true;
+  }
   default:
     return ExprEvaluatorBaseTy::VisitCallExpr(E);
   }
@@ -5853,8 +5949,6 @@ public:
   bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
 
 private:
-  CharUnits GetAlignOfExpr(const Expr *E);
-  CharUnits GetAlignOfType(QualType T);
   static QualType GetObjectType(APValue::LValueBase B);
   bool TryEvaluateBuiltinObjectSize(const CallExpr *E);
   // FIXME: Missing: array subscript of vector, member of vector
@@ -7019,39 +7113,6 @@ bool IntExprEvaluator::VisitBinaryOperat
   return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
 }
 
-CharUnits IntExprEvaluator::GetAlignOfType(QualType T) {
-  // C++ [expr.alignof]p3:
-  //     When alignof is applied to a reference type, the result is the
-  //     alignment of the referenced type.
-  if (const ReferenceType *Ref = T->getAs<ReferenceType>())
-    T = Ref->getPointeeType();
-
-  // __alignof is defined to return the preferred alignment.
-  return Info.Ctx.toCharUnitsFromBits(
-    Info.Ctx.getPreferredTypeAlign(T.getTypePtr()));
-}
-
-CharUnits IntExprEvaluator::GetAlignOfExpr(const Expr *E) {
-  E = E->IgnoreParens();
-
-  // The kinds of expressions that we have special-case logic here for
-  // should be kept up to date with the special checks for those
-  // expressions in Sema.
-
-  // alignof decl is always accepted, even if it doesn't make sense: we default
-  // to 1 in those cases.
-  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
-    return Info.Ctx.getDeclAlign(DRE->getDecl(),
-                                 /*RefAsPointee*/true);
-
-  if (const MemberExpr *ME = dyn_cast<MemberExpr>(E))
-    return Info.Ctx.getDeclAlign(ME->getMemberDecl(),
-                                 /*RefAsPointee*/true);
-
-  return GetAlignOfType(E->getType());
-}
-
-
 /// VisitUnaryExprOrTypeTraitExpr - Evaluate a sizeof, alignof or vec_step with
 /// a result as the expression's type.
 bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
@@ -7059,9 +7120,9 @@ bool IntExprEvaluator::VisitUnaryExprOrT
   switch(E->getKind()) {
   case UETT_AlignOf: {
     if (E->isArgumentType())
-      return Success(GetAlignOfType(E->getArgumentType()), E);
+      return Success(GetAlignOfType(Info, E->getArgumentType()), E);
     else
-      return Success(GetAlignOfExpr(E->getArgumentExpr()), E);
+      return Success(GetAlignOfExpr(Info, E->getArgumentExpr()), E);
   }
 
   case UETT_VecStep: {

Added: cfe/trunk/test/SemaCXX/builtin-assume-aligned.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/builtin-assume-aligned.cpp?rev=218992&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/builtin-assume-aligned.cpp (added)
+++ cfe/trunk/test/SemaCXX/builtin-assume-aligned.cpp Fri Oct  3 12:18:37 2014
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -triple x86_64-linux-gnu %s
+
+int n;
+constexpr int *p = 0;
+// expected-error at +1 {{must be initialized by a constant expression}}
+constexpr int *k = (int *) __builtin_assume_aligned(p, 16, n = 5);
+
+constexpr void *l = __builtin_assume_aligned(p, 16);
+
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{cast from 'void *' is not allowed in a constant expression}}
+constexpr int *c = (int *) __builtin_assume_aligned(p, 16);
+
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{alignment of the base pointee object (4 bytes) is less than the asserted 16 bytes}}
+constexpr void *m = __builtin_assume_aligned(&n, 16);
+
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{offset of the aligned pointer from the base pointee object (-2 bytes) is not a multiple of the asserted 4 bytes}}
+constexpr void *q1 = __builtin_assume_aligned(&n, 4, 2);
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{offset of the aligned pointer from the base pointee object (2 bytes) is not a multiple of the asserted 4 bytes}}
+constexpr void *q2 = __builtin_assume_aligned(&n, 4, -2);
+constexpr void *q3 = __builtin_assume_aligned(&n, 4, 4);
+constexpr void *q4 = __builtin_assume_aligned(&n, 4, -4);
+
+static char ar1[6];
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{alignment of the base pointee object (1 byte) is less than the asserted 16 bytes}}
+constexpr void *r1 = __builtin_assume_aligned(&ar1[2], 16);
+
+static char ar2[6] __attribute__((aligned(32)));
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{offset of the aligned pointer from the base pointee object (2 bytes) is not a multiple of the asserted 16 bytes}}
+constexpr void *r2 = __builtin_assume_aligned(&ar2[2], 16);
+constexpr void *r3 = __builtin_assume_aligned(&ar2[2], 16, 2);
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{offset of the aligned pointer from the base pointee object (1 byte) is not a multiple of the asserted 16 bytes}}
+constexpr void *r4 = __builtin_assume_aligned(&ar2[2], 16, 1);
+
+constexpr int* x = __builtin_constant_p((int*)0xFF) ? (int*)0xFF : (int*)0xFF;
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{value of the aligned pointer (255) is not a multiple of the asserted 32 bytes}}
+constexpr void *s1 = __builtin_assume_aligned(x, 32);
+// expected-error at +2 {{must be initialized by a constant expression}}
+// expected-note at +1 {{value of the aligned pointer (250) is not a multiple of the asserted 32 bytes}}
+constexpr void *s2 = __builtin_assume_aligned(x, 32, 5);
+constexpr void *s3 = __builtin_assume_aligned(x, 32, -1);
+





More information about the cfe-commits mailing list