george.burgess.iv created this revision.
george.burgess.iv added a reviewer: rsmith.
george.burgess.iv added a subscriber: cfe-commits.

This is the first in a two patch series aimed at adding the alloc_size 
attribute to clang. This patch adds the alloc_size attribute, docs, and all of 
the logic needed to handle it in ExprConstant. The only thing that's _really_ 
missing is our ability to handle non-const locals, because that's proving to be 
a moderately interesting challenge. (Spoiler alert: that's basically the only 
thing in patch #2)

There's also a restructuring of TryEvaluateBuiltinObjectSize thrown in, because 
I felt like it was getting overly subtle. Hopefully the new version is 
considerably easier to reason about. :)

— Implementation notes —
- With the restructuring of TryEvaluateBuiltinObjectSize, I was able to make 
EM_DesignatorFold (now EM_OffsetFold) the only EvalMode we use in evaluating 
__builtin_object_size.

- The InvalidBase functionality of LValues was further (ab)used to get 
information to TryEvaluateBuiltinObjectSize. We know an LValue has been 
initialized by an alloc_size function if the base is invalid and the base 
itself is a CallExpr. This won’t conflict with current behavior, because (prior 
to this patch) all invalid bases were MemberExprs.

- As said in the ExprConstant code, I’d love a less hand-wavy approach to 
writing tryEvaluateLValueAsAllocSize.

http://reviews.llvm.org/D14274

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/ExprConstant.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/CodeGen/alloc-size.c
  test/Sema/alloc-size.c

Index: test/Sema/alloc-size.c
===================================================================
--- /dev/null
+++ test/Sema/alloc-size.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 %s -verify
+
+void *fail1(int a) __attribute__((alloc_size)); //expected-error{{'alloc_size' attribute takes at least 1 argument}}
+void *fail2(int a) __attribute__((alloc_size())); //expected-error{{'alloc_size' attribute takes at least 1 argument}}
+
+void *fail3(int a) __attribute__((alloc_size(0))); //expected-error{{'alloc_size' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
+void *fail4(int a) __attribute__((alloc_size(2))); //expected-error{{'alloc_size' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
+
+void *fail5(int a, int b) __attribute__((alloc_size(0, 1))); //expected-error{{'alloc_size' attribute parameter 1 is out of bounds: must be between 1 and 2}}
+void *fail6(int a, int b) __attribute__((alloc_size(3, 1))); //expected-error{{'alloc_size' attribute parameter 1 is out of bounds: must be between 1 and 2}}
+
+void *fail7(int a, int b) __attribute__((alloc_size(1, 0))); //expected-error{{'alloc_size' attribute parameter 2 is out of bounds: must be between 1 and 2}}
+void *fail8(int a, int b) __attribute__((alloc_size(1, 3))); //expected-error{{'alloc_size' attribute parameter 2 is out of bounds: must be between 1 and 2}}
+
+int fail9(int a) __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to return values that are pointers}}
+
+int fail10 __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attribute only applies to functions}}
+
+void *fail11(void *a) __attribute__((alloc_size(1))); //expected-error{{'alloc_size' attribute only applies to integer arguments}}
+
+void *fail12(int a) __attribute__((alloc_size("abc"))); //expected-error{{'alloc_size' attribute requires parameter 1 to be an integer constant}}
+void *fail12(int a) __attribute__((alloc_size(1, "abc"))); //expected-error{{'alloc_size' attribute requires parameter 2 to be an integer constant}}
+void *fail13(int a) __attribute__((alloc_size(1U<<31))); //expected-error{{integer constant expression evaluates to value 2147483648 that cannot be represented in a 32-bit signed integer type}}
Index: test/CodeGen/alloc-size.c
===================================================================
--- /dev/null
+++ test/CodeGen/alloc-size.c
@@ -0,0 +1,263 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck %s
+
+#define NULL ((void *)0)
+
+int gi;
+
+void *my_malloc(int size) __attribute__((alloc_size(1)));
+void *my_calloc(int elem_size, int num_elems) __attribute__((alloc_size(1, 2)));
+
+// CHECK-LABEL: @test1
+void test1() {
+  void *const vp = my_malloc(100);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(vp, 0);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(vp, 1);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(vp, 2);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(vp, 3);
+
+  void *const arr = my_calloc(100, 5);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(arr, 0);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(arr, 1);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(arr, 2);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(arr, 3);
+
+  // CHECK: store i32 100
+  gi = __builtin_object_size(my_malloc(100), 0);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(my_malloc(100), 1);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(my_malloc(100), 2);
+  // CHECK: store i32 100
+  gi = __builtin_object_size(my_malloc(100), 3);
+
+  // CHECK: store i32 500
+  gi = __builtin_object_size(my_calloc(100, 5), 0);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(my_calloc(100, 5), 1);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(my_calloc(100, 5), 2);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(my_calloc(100, 5), 3);
+}
+
+// CHECK-LABEL: @test2
+void test2() {
+  void *const vp = my_malloc(gi);
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(vp, 0);
+
+  void *const arr1 = my_calloc(gi, 1);
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(arr1, 0);
+
+  void *const arr2 = my_calloc(1, gi);
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(arr2, 0);
+}
+
+// CHECK-LABEL: @test3
+void test3() {
+  char *const buf = (char *)my_calloc(100, 5);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(buf, 0);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(buf, 1);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(buf, 2);
+  // CHECK: store i32 500
+  gi = __builtin_object_size(buf, 3);
+}
+
+struct Data {
+  int a;
+  int t[10];
+  char pad[3];
+  char end[1];
+};
+
+// CHECK-LABEL: @test5
+void test5() {
+  struct Data *const data = my_malloc(sizeof(*data));
+  // CHECK: store i32 48
+  gi = __builtin_object_size(data, 0);
+  // CHECK: store i32 48
+  gi = __builtin_object_size(data, 1);
+  // CHECK: store i32 48
+  gi = __builtin_object_size(data, 2);
+  // CHECK: store i32 48
+  gi = __builtin_object_size(data, 3);
+
+  // CHECK: store i32 40
+  gi = __builtin_object_size(&data->t[1], 0);
+  // CHECK: store i32 36
+  gi = __builtin_object_size(&data->t[1], 1);
+  // CHECK: store i32 40
+  gi = __builtin_object_size(&data->t[1], 2);
+  // CHECK: store i32 36
+  gi = __builtin_object_size(&data->t[1], 3);
+
+  struct Data *const arr = my_calloc(sizeof(*data), 2);
+  // CHECK: store i32 96
+  gi = __builtin_object_size(arr, 0);
+  // CHECK: store i32 96
+  gi = __builtin_object_size(arr, 1);
+  // CHECK: store i32 96
+  gi = __builtin_object_size(arr, 2);
+  // CHECK: store i32 96
+  gi = __builtin_object_size(arr, 3);
+
+  // CHECK: store i32 88
+  gi = __builtin_object_size(&arr->t[1], 0);
+  // CHECK: store i32 36
+  gi = __builtin_object_size(&arr->t[1], 1);
+  // CHECK: store i32 88
+  gi = __builtin_object_size(&arr->t[1], 2);
+  // CHECK: store i32 36
+  gi = __builtin_object_size(&arr->t[1], 3);
+}
+
+// CHECK-LABEL: @test6
+void test6() {
+  // Things that would normally trigger conservative estimates don't need to do
+  // so when we know the source of the allocation.
+  struct Data *const data = my_malloc(sizeof(*data) + 10);
+  // CHECK: store i32 11
+  gi = __builtin_object_size(data->end, 0);
+  // CHECK: store i32 11
+  gi = __builtin_object_size(data->end, 1);
+  // CHECK: store i32 11
+  gi = __builtin_object_size(data->end, 2);
+  // CHECK: store i32 11
+  gi = __builtin_object_size(data->end, 3);
+
+  struct Data *const arr = my_calloc(sizeof(*arr) + 5, 3);
+  // It's unclear what the best path is here.
+  //
+  // First, if we're writing off the end and we're in an array, then the user's
+  // intent gets a lot less clear. Consider:
+  //   struct Data { char end[1]; };
+  //   struct Data *D = calloc(sizeof(*D) + 10, 2);
+  //   __builtin_object_size(D[0]->end, 1);
+  // We can't reasonably determine whether the user will consider the 0th
+  // element of D to be at ((char*)D + sizeof(*D)), or ((char*)D + sizeof(*D)
+  // + 10).
+  //
+  // Next, many C users see this:
+  //   struct Foo { /* [snip] */ };
+  //   void *p = malloc(sizeof(struct Foo) * 100);
+  //   memset(p, '\0', sizeof(struct Foo) * 100);
+  //
+  // ...As identical to this:
+  //   struct Foo { /* [snip] */ };
+  //   void *p = calloc(sizeof(struct Foo), 100);
+  //
+  // ...So acting differently in these cases may be confusing.
+  //
+  // Therefore, I think the best path is to treat them as identical, since we're
+  // guaranteed that the bytes, at the very least, exist, and there's no clear
+  // object boundaries to speak of. Note that all of this only applies if we've
+  // hit the special "we're trying to write off the end of an object" logic.
+
+  // CHECK: store i32 112
+  gi = __builtin_object_size(arr[0].end, 0);
+  // CHECK: store i32 112
+  gi = __builtin_object_size(arr[0].end, 1);
+  // CHECK: store i32 112
+  gi = __builtin_object_size(arr[0].end, 2);
+  // CHECK: store i32 112
+  gi = __builtin_object_size(arr[0].end, 3);
+
+  // CHECK: store i32 64
+  gi = __builtin_object_size(arr[1].end, 0);
+  // CHECK: store i32 64
+  gi = __builtin_object_size(arr[1].end, 1);
+  // CHECK: store i32 64
+  gi = __builtin_object_size(arr[1].end, 2);
+  // CHECK: store i32 64
+  gi = __builtin_object_size(arr[1].end, 3);
+
+  // CHECK: store i32 16
+  gi = __builtin_object_size(arr[2].end, 0);
+  // CHECK: store i32 16
+  gi = __builtin_object_size(arr[2].end, 1);
+  // CHECK: store i32 16
+  gi = __builtin_object_size(arr[2].end, 2);
+  // CHECK: store i32 16
+  gi = __builtin_object_size(arr[2].end, 3);
+}
+
+// CHECK-LABEL: @test7
+void test7() {
+  // Note: sizeof(struct Data) - 2*sizeof(int) == 40
+
+  // Chop the char arrays + final int of t[10] off the end.
+  struct Data *const data = my_malloc(sizeof(*data)-2*sizeof(int));
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&data->t[1], 0);
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&data->t[1], 1);
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&data->t[1], 2);
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&data->t[1], 3);
+
+  // CHECK: store i32 0
+  gi = __builtin_object_size(data->pad, 0);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(data->pad, 1);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(data->pad, 2);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(data->pad, 3);
+
+  struct Data *const arr = my_calloc(sizeof(*data)-sizeof(int), 2);
+  // CHECK: store i32 80
+  gi = __builtin_object_size(&arr[0].t[1], 0);
+  // CHECK: store i32 36
+  gi = __builtin_object_size(&arr[0].t[1], 1);
+  // CHECK: store i32 80
+  gi = __builtin_object_size(&arr[0].t[1], 2);
+  // CHECK: store i32 36
+  gi = __builtin_object_size(&arr[0].t[1], 3);
+
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&arr[1].t[1], 0);
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&arr[1].t[1], 1);
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&arr[1].t[1], 2);
+  // CHECK: store i32 32
+  gi = __builtin_object_size(&arr[1].t[1], 3);
+
+  // CHECK: store i32 0
+  gi = __builtin_object_size(arr[1].pad, 0);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(arr[1].pad, 1);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(arr[1].pad, 2);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(arr[1].pad, 3);
+}
+
+// CHECK-LABEL: @test8
+void test8() {
+  // Non-const pointers aren't currently supported. They (hopefully) will be in
+  // the near future.
+  void *buf = my_calloc(100, 5);
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(buf, 0);
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+  gi = __builtin_object_size(buf, 1);
+  // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
+  gi = __builtin_object_size(buf, 2);
+  // CHECK: store i32 0
+  gi = __builtin_object_size(buf, 3);
+}
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -705,6 +705,98 @@
                                      Attr.getAttributeSpellingListIndex()));
 }
 
+/// Checks to be sure that the given parameter number is inbounds, and is an
+/// some integral type. Will emit appropriate diagnostics if this returns false.
+///
+/// FuncParamNo is expected to be from the user, so is base-1. AttrArgNo is used
+/// to actually retrieve the argument, so it's base-0.
+static bool checkParamIsIntegerType(Sema &S, const FunctionProtoType *FPT,
+                                    const AttributeList &Attr,
+                                    unsigned FuncParamNo, unsigned AttrArgNo) {
+  assert(Attr.getArg(AttrArgNo).is<Expr *>());
+  // FuncParamNo is base-1
+  if (FuncParamNo < 1 || FuncParamNo > FPT->getNumParams()) {
+    auto SrcLoc = Attr.getArgAsExpr(AttrArgNo)->getLocStart();
+    auto UserArgNo = AttrArgNo + 1;
+    S.Diag(SrcLoc, diag::err_attribute_argument_out_of_range)
+        << Attr.getName() << UserArgNo << FPT->getNumParams();
+    return false;
+  }
+
+  if (!FPT->getParamType(FuncParamNo - 1)->isIntegerType()) {
+    auto SrcLoc = Attr.getArgAsExpr(AttrArgNo)->getLocStart();
+    S.Diag(SrcLoc, diag::err_attribute_integers_only) << Attr.getName();
+    return false;
+  }
+  return true;
+}
+
+/// \brief Wrapper around checkUInt32Argument, with an extra check to be sure
+/// that the result will fit into a regular (signed) int. All args have the same
+/// purpose as they do in checkUInt32Argument.
+static bool checkPositiveIntArgument(Sema &S, const AttributeList &Attr,
+                                     const Expr *Expr, int &Val,
+                                     unsigned Idx = UINT_MAX) {
+  uint32_t UVal;
+  if (!checkUInt32Argument(S, Attr, Expr, UVal, Idx))
+    return false;
+
+  if (UVal > std::numeric_limits<int>::max()) {
+    llvm::APSInt I(32); // for toString
+    I = UVal;
+    S.Diag(Expr->getExprLoc(), diag::err_ice_too_large)
+        << I.toString(10, false) << 32 << /* Unsigned */ 0;
+    return false;
+  }
+
+  Val = (int)UVal;
+  return true;
+}
+
+static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  if (!checkAttributeAtLeastNumArgs(S, Attr, 1) ||
+      !checkAttributeAtMostNumArgs(S, Attr, 2))
+    return;
+
+  auto *FD = dyn_cast<FunctionDecl>(D);
+  if (FD == nullptr || !FD->hasPrototype()) {
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+        << Attr.getName() << 4;
+    return;
+  }
+
+  auto *FPT = FD->getType()->castAs<FunctionProtoType>();
+  if (!FPT->getReturnType()->isPointerType()) {
+    S.Diag(Attr.getLoc(), diag::warn_attribute_return_pointers_only)
+        << Attr.getName();
+    return;
+  }
+
+  auto *SizeExpr = Attr.getArgAsExpr(0);
+  int SizeArgNo;
+  // Paramater indices are 1-based, hence Index=1
+  if (!checkPositiveIntArgument(S, Attr, SizeExpr, SizeArgNo, /*Index=*/1))
+    return;
+
+  if (!checkParamIsIntegerType(S, FPT, Attr, SizeArgNo, /*AttrArgNo=*/0))
+    return;
+
+  // Args are 1-based, so 0 implies that the arg was not present
+  int NumberArgNo = 0;
+  if (Attr.getNumArgs() == 2) {
+    auto *NumberExpr = Attr.getArgAsExpr(1);
+    // Paramater indices are 1-based, hence Index=2
+    if (!checkPositiveIntArgument(S, Attr, NumberExpr, NumberArgNo,
+                                  /*Index=*/2))
+      return;
+    if (!checkParamIsIntegerType(S, FPT, Attr, NumberArgNo, /*AttrArgNo=*/1))
+      return;
+  }
+
+  D->addAttr(::new (S.Context) AllocSizeAttr(
+      Attr.getRange(), S.Context, SizeArgNo, NumberArgNo,
+      Attr.getAttributeSpellingListIndex()));
+}
 
 static bool checkTryLockFunAttrCommon(Sema &S, Decl *D,
                                       const AttributeList &Attr,
@@ -4753,6 +4845,9 @@
   case AttributeList::AT_AlignValue:
     handleAlignValueAttr(S, D, Attr);
     break;
+  case AttributeList::AT_AllocSize:
+    handleAllocSizeAttr(S, D, Attr);
+    break;
   case AttributeList::AT_AlwaysInline:
     handleAlwaysInlineAttr(S, D, Attr);
     break;
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -513,9 +513,14 @@
       /// gets a chance to look at it.
       EM_PotentialConstantExpressionUnevaluated,
 
-      /// Evaluate as a constant expression. Continue evaluating if we find a
-      /// MemberExpr with a base that can't be evaluated.
-      EM_DesignatorFold,
+      /// Evaluate as a constant expression. Continue evaluating if either:
+      /// - We find a MemberExpr with a base that can't be evaluated.
+      /// - We find a variable initialized with a call to a function that has
+      ///   the alloc_size attribute on it.
+      /// In either case, the LValue returned shall have an invalid base; in the
+      /// former, the base will be the invalid MemberExpr, in the latter, the
+      /// base will be the CallExpr.
+      EM_OffsetFold,
     } EvalMode;
 
     /// Are we checking whether the expression is a potential constant
@@ -618,7 +623,7 @@
           case EM_PotentialConstantExpression:
           case EM_ConstantExpressionUnevaluated:
           case EM_PotentialConstantExpressionUnevaluated:
-          case EM_DesignatorFold:
+          case EM_OffsetFold:
             HasActiveDiagnostic = false;
             return OptionalDiagnostic();
           }
@@ -698,7 +703,7 @@
       case EM_ConstantExpression:
       case EM_ConstantExpressionUnevaluated:
       case EM_ConstantFold:
-      case EM_DesignatorFold:
+      case EM_OffsetFold:
         return false;
       }
       llvm_unreachable("Missed EvalMode case");
@@ -727,14 +732,14 @@
       case EM_ConstantExpressionUnevaluated:
       case EM_ConstantFold:
       case EM_IgnoreSideEffects:
-      case EM_DesignatorFold:
+      case EM_OffsetFold:
         return false;
       }
       llvm_unreachable("Missed EvalMode case");
     }
 
     bool allowInvalidBaseExpr() const {
-      return EvalMode == EM_DesignatorFold;
+      return EvalMode == EM_OffsetFold;
     }
   };
 
@@ -771,11 +776,10 @@
   struct FoldOffsetRAII {
     EvalInfo &Info;
     EvalInfo::EvaluationMode OldMode;
-    explicit FoldOffsetRAII(EvalInfo &Info, bool Subobject)
+    explicit FoldOffsetRAII(EvalInfo &Info)
         : Info(Info), OldMode(Info.EvalMode) {
       if (!Info.checkingPotentialConstantExpression())
-        Info.EvalMode = Subobject ? EvalInfo::EM_DesignatorFold
-                                  : EvalInfo::EM_ConstantFold;
+        Info.EvalMode = EvalInfo::EM_OffsetFold;
     }
 
     ~FoldOffsetRAII() { Info.EvalMode = OldMode; }
@@ -4824,6 +4828,129 @@
 // Pointer Evaluation
 //===----------------------------------------------------------------------===//
 
+static bool isCallToAllocSizeFunction(const CallExpr *E) {
+  auto *Callee = E->getDirectCallee();
+  return Callee != nullptr && Callee->hasAttr<AllocSizeAttr>();
+}
+
+static bool isBaseAnAllocSizeCall(const LValue &LVal) {
+  auto *Base = LVal.getLValueBase().dyn_cast<const Expr *>();
+  auto *CE = dyn_cast_or_null<CallExpr>(Base);
+  return CE != nullptr && isCallToAllocSizeFunction(CE);
+}
+
+static bool getNumBytesReturnedByAllocSizeFunction(const ASTContext &Ctx,
+                                                   const LValue &LVal,
+                                                   CharUnits &Result) {
+  assert(isBaseAnAllocSizeCall(LVal));
+  auto *Call = cast<CallExpr>(LVal.getLValueBase().get<const Expr *>());
+  auto *AllocSize = Call->getCalleeDecl()->getAttr<AllocSizeAttr>();
+
+  // alloc_size args are base-1, 0 means not present.
+  assert(AllocSize->getElemSizeParam() != 0);
+  unsigned SizeArgNo = AllocSize->getElemSizeParam() - 1;
+  unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
+  if (Call->getNumArgs() <= SizeArgNo)
+    return false;
+
+  APSInt SizeOfElem(/*BitWidth=*/BitsInSizeT, /*IsUnsigned=*/true);
+  if (!Call->getArg(SizeArgNo)->EvaluateAsInt(
+          SizeOfElem, Ctx, /*AllowSideEffects=*/Expr::SE_AllowSideEffects))
+    return false;
+
+  bool WasNumArgSpecified = AllocSize->getNumElemsParam() != 0;
+  if (!WasNumArgSpecified) {
+    Result = CharUnits::fromQuantity(SizeOfElem.getZExtValue());
+    return true;
+  }
+
+  APSInt NumberOfElems(/*BitWidth=*/BitsInSizeT, /*IsUnsigned=*/true);
+  // Argument numbers start at 1
+  unsigned NumArgNo = AllocSize->getNumElemsParam() - 1;
+  if (!Call->getArg(NumArgNo)->EvaluateAsInt(
+          NumberOfElems, Ctx, /*AllowSideEffects=*/Expr::SE_AllowSideEffects))
+    return false;
+
+  bool Overflow = false;
+  auto BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow);
+  if (Overflow)
+    return false;
+
+  Result = CharUnits::fromQuantity(BytesAvailable.getZExtValue());
+  return true;
+}
+
+// Attempts to evaluate the given LValue as the result of a call to a function
+// with the alloc_size attribute on it. This is an extremely fun case, as it is
+// very different from many things we try to handle in ExprConstant (e.g. "we
+// know that this is a block of memory, but can make no guarantees about its
+// value").  Also, it requires a bit of whole-function static analysis, because
+// code like the following needs to be correctly handled:
+//
+// void foo() {
+//   void *ptr = malloc(sizeof(int));
+//   for (int i = 0; i < 2; i++) {
+//     printf("%ld\n", __builtin_object_size(ptr, 0));
+//     free(ptr);
+//     ptr = malloc(sizeof(int) * 2);
+//   }
+// }
+//
+// Where "correct", in our current implementation, means "fails".
+static bool tryEvaluateLValueAsAllocSize(const Expr *E, LValue &Result,
+                                         EvalInfo &Info) {
+  auto ExtractCallExpr = [&Info](const Expr *E) -> const CallExpr *{
+    // A *very* common idiom with e.g. malloc is:
+    // struct Foo *f = (struct Foo *)malloc(sizeof(*f));
+    // We want to succeed on the common case, so we ignore parens/implicit
+    // casts, and, if the function returns void*, a single cast of any kind.
+    auto *MaybeCast = E->IgnoreParenImpCasts();
+    // Ignore one explicit cast
+    auto *Explicit = dyn_cast<CastExpr>(MaybeCast);
+    if (Explicit == nullptr)
+      return dyn_cast<CallExpr>(MaybeCast);
+
+    const Expr *MaybeCall = Explicit->getSubExpr()->IgnoreParenImpCasts();
+    if (auto *Call = dyn_cast<CallExpr>(MaybeCall))
+      if (Call->getCallReturnType(Info.Ctx)->isVoidPointerType())
+        return Call;
+    return nullptr;
+  };
+
+  assert(E->isGLValue());
+  if (!Info.allowInvalidBaseExpr())
+    return false;
+
+  // We're specifically looking for a varaible initialized by a function with
+  // the alloc_size attribute. This requires a lot of guesswork and hand-waving.
+  // FIXME: Code-review note: I would *love* suggestions for a better way to do
+  // this.
+  auto *DRE = dyn_cast<DeclRefExpr>(E);
+  if (DRE == nullptr)
+    return false;
+
+  auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+  // Don't support parameters (they can be assigned without us knowing) or
+  // static variables (use-before-assign isn't UB)
+  if (VD == nullptr || !VD->isLocalVarDecl())
+    return false;
+
+  // We currently don't support non-constant variables.
+  if (!VD->getType().isConstQualified())
+    return false;
+
+  const Expr *Init = VD->getAnyInitializer();
+  if (Init == nullptr)
+    return false;
+
+  const CallExpr *Call = ExtractCallExpr(Init);
+  if (Call == nullptr || !isCallToAllocSizeFunction(Call))
+    return false;
+
+  Result.setInvalid(Call);
+  return true;
+}
+
 namespace {
 class PointerExprEvaluator
   : public ExprEvaluatorBase<PointerExprEvaluator> {
@@ -5009,6 +5136,19 @@
 
   case CK_FunctionToPointerDecay:
     return EvaluateLValue(SubExpr, Result, Info);
+
+  case CK_LValueToRValue: {
+    LValue LVal;
+    if (!EvaluateLValue(E->getSubExpr(), LVal, Info))
+      return tryEvaluateLValueAsAllocSize(E->getSubExpr(), Result, Info);
+
+    APValue RVal;
+    // Note, we use the subexpression's type in order to retain cv-qualifiers.
+    if (!handleLValueToRValueConversion(Info, E, E->getSubExpr()->getType(),
+                                        LVal, RVal))
+      return tryEvaluateLValueAsAllocSize(E->getSubExpr(), Result, Info);
+    return Success(RVal, E);
+  }
   }
 
   return ExprEvaluatorBaseTy::VisitCastExpr(E);
@@ -5119,7 +5259,13 @@
     return true;
   }
   default:
-    return ExprEvaluatorBaseTy::VisitCallExpr(E);
+    if (ExprEvaluatorBaseTy::VisitCallExpr(E))
+      return true;
+    if (isCallToAllocSizeFunction(E)) {
+      Result.setInvalid(E);
+      return true;
+    }
+    return false;
   }
 }
 
@@ -6372,9 +6518,119 @@
     return true;
 
   auto *E = LVal.Base.dyn_cast<const Expr *>();
-  (void)E;
-  assert(E != nullptr && isa<MemberExpr>(E));
-  return false;
+  return E == nullptr || !isa<MemberExpr>(E);
+}
+
+// Helper for TryEvaluateBuiltinObjectSize -- Given an LValue, this will
+// determine how many bytes exist from the beginning of the object to either the
+// end of the current subobject, or the end of the object itself, depending on
+// what the LValue looks like + the value of Type.
+static bool determineEndOffset(EvalInfo &Info, const CallExpr *E, unsigned Type,
+                               const LValue &Base, CharUnits &Result) {
+  // In the case where we're not dealing with a subobject in the expression, we
+  // ignore the subobject bit.
+  bool SubobjectOnly = (Type & 1) != 0 && !refersToCompleteObject(Base);
+
+  CharUnits AllocSizeBytes;
+  bool IsAllocSizeBase = isBaseAnAllocSizeCall(Base);
+  if (IsAllocSizeBase)
+    if (!getNumBytesReturnedByAllocSizeFunction(Info.Ctx, Base, AllocSizeBytes))
+      return false;
+
+  // We want to evaluate the size of the entire object
+  if (!SubobjectOnly || Base.Designator.Invalid) {
+    if (IsAllocSizeBase) {
+      Result = AllocSizeBytes;
+      return true;
+    }
+
+    // If we can't find the designator and we need to give a lower-bound, then
+    // we can't do anything. If we need to give an upper-bound (i.e. Type==1),
+    // we can fall back to evaluating the full object later.
+    if (Base.Designator.Invalid && Type == 3) {
+      assert(SubobjectOnly);
+      return false;
+    }
+
+    if (Base.InvalidBase)
+      return false;
+
+    QualType BaseTy = getObjectType(Base.getLValueBase());
+    if (BaseTy.isNull())
+      return false;
+
+    return HandleSizeof(Info, E->getExprLoc(), BaseTy, Result);
+  }
+
+  // We want to evaluate the size of a subobject.
+  const auto &Designator = Base.Designator;
+
+  // The following is a moderately common idiom in C:
+  //
+  // struct Foo { int a; char c[1]; };
+  // struct Foo *F = (struct Foo *)malloc(sizeof(struct Foo) + strlen(Bar));
+  // strcpy(&F->c[0], Bar);
+  //
+  // So, if we see that we're examining a 1-length (or 0-length) array at the
+  // end of a struct with an unknown base, we'll either pretend we're
+  // evaluating a full object (this is fine because the designator is at the
+  // end), or we'll give up. If we can't determine how many bytes are available,
+  // then we need to give up if Type==1, because we can't correctly provide an
+  // upper bound. We can, however always, provide a lower bound of 1 for
+  // Type==3.
+  //
+  // Note that this may make our behavior a bit wonky when interacting with
+  // calloc and type=3. Consider:
+  //   struct Data { int i; char chars[1]; };
+  //   struct Data *D = calloc(sizeof(*D), 2);
+  //   __builtin_object_size(D.chars, 3) == 9;
+  // There's a full argument for why this is the least-bad of all alternatives
+  // in test/CodeGen/alloc-size.c (test #6). I just wanted to note this locally.
+  if (Base.InvalidBase &&
+      Designator.Entries.size() == Designator.MostDerivedPathLength &&
+      Designator.MostDerivedIsArrayElement &&
+      Designator.MostDerivedArraySize < 2 &&
+      isDesignatorAtObjectEnd(Info.Ctx, Base)) {
+    if (IsAllocSizeBase) {
+      Result = AllocSizeBytes;
+      return true;
+    }
+    if (Type == 1)
+      return false;
+    // Type==3 uses the standard behavior if we don't have alloc_size.
+  }
+
+  CharUnits BytesPerElem;
+  if (!HandleSizeof(Info, E->getExprLoc(), Designator.MostDerivedType,
+                    BytesPerElem))
+    return false;
+
+  // According to the GCC documentation, we want the size of the subobject
+  // denoted by the pointer. But that's not quite right -- what we actually
+  // want is the size of the immediately-enclosing array, if there is one.
+  int64_t ElemsRemaining = 1;
+  if (Designator.MostDerivedIsArrayElement &&
+      Designator.Entries.size() == Designator.MostDerivedPathLength)
+    ElemsRemaining =
+        Designator.MostDerivedArraySize - Designator.Entries.back().ArrayIndex;
+  else if (Designator.isOnePastTheEnd())
+    ElemsRemaining = 0;
+
+  if (ElemsRemaining <= 0) {
+    // We've gone passed the end -- pretend there were zero bytes.
+    Result = CharUnits::Zero();
+    return true;
+  }
+
+  CharUnits BytesRemainingInArray = BytesPerElem * ElemsRemaining;
+  CharUnits SubobjectBytes = Base.Offset + BytesRemainingInArray;
+  // We need to be correct even if the user allocates less bytes than the type
+  // requires.
+  if (IsAllocSizeBase)
+    Result = std::min(SubobjectBytes, AllocSizeBytes);
+  else
+    Result = SubobjectBytes;
+  return true;
 }
 
 bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E,
@@ -6386,7 +6642,7 @@
     // If there are any, but we can determine the pointed-to object anyway, then
     // ignore the side-effects.
     SpeculativeEvaluationRAII SpeculativeEval(Info);
-    FoldOffsetRAII Fold(Info, Type & 1);
+    FoldOffsetRAII Fold(Info);
     const Expr *Ptr = ignorePointerCastsAndParens(E->getArg(0));
     if (!EvaluatePointer(Ptr, Base, Info))
       return false;
@@ -6398,87 +6654,8 @@
   if (BaseOffset.isNegative())
     return Success(0, E);
 
-  // In the case where we're not dealing with a subobject, we discard the
-  // subobject bit.
-  bool SubobjectOnly = (Type & 1) != 0 && !refersToCompleteObject(Base);
-
-  // If Type & 1 is 0, we need to be able to statically guarantee that the bytes
-  // exist. If we can't verify the base, then we can't do that.
-  //
-  // As a special case, we produce a valid object size for an unknown object
-  // with a known designator if Type & 1 is 1. For instance:
-  //
-  //   extern struct X { char buff[32]; int a, b, c; } *p;
-  //   int a = __builtin_object_size(p->buff + 4, 3); // returns 28
-  //   int b = __builtin_object_size(p->buff + 4, 2); // returns 0, not 40
-  //
-  // This matches GCC's behavior.
-  if (Base.InvalidBase && !SubobjectOnly)
-    return Error(E);
-
-  // If we're not examining only the subobject, then we reset to a complete
-  // object designator
-  //
-  // If Type is 1 and we've lost track of the subobject, just find the complete
-  // object instead. (If Type is 3, that's not correct behavior and we should
-  // return 0 instead.)
-  LValue End = Base;
-  if (!SubobjectOnly || (End.Designator.Invalid && Type == 1)) {
-    QualType T = getObjectType(End.getLValueBase());
-    if (T.isNull())
-      End.Designator.setInvalid();
-    else {
-      End.Designator = SubobjectDesignator(T);
-      End.Offset = CharUnits::Zero();
-    }
-  }
-
-  // If it is not possible to determine which objects ptr points to at compile
-  // time, __builtin_object_size should return (size_t) -1 for type 0 or 1
-  // and (size_t) 0 for type 2 or 3.
-  if (End.Designator.Invalid)
-    return false;
-
-  // According to the GCC documentation, we want the size of the subobject
-  // denoted by the pointer. But that's not quite right -- what we actually
-  // want is the size of the immediately-enclosing array, if there is one.
-  int64_t AmountToAdd = 1;
-  if (End.Designator.MostDerivedIsArrayElement &&
-      End.Designator.Entries.size() == End.Designator.MostDerivedPathLength) {
-    // We got a pointer to an array. Step to its end.
-    AmountToAdd = End.Designator.MostDerivedArraySize -
-      End.Designator.Entries.back().ArrayIndex;
-  } else if (End.Designator.isOnePastTheEnd()) {
-    // We're already pointing at the end of the object.
-    AmountToAdd = 0;
-  }
-
-  QualType PointeeType = End.Designator.MostDerivedType;
-  assert(!PointeeType.isNull());
-  if (PointeeType->isIncompleteType() || PointeeType->isFunctionType())
-    return Error(E);
-
-  if (!HandleLValueArrayAdjustment(Info, E, End, End.Designator.MostDerivedType,
-                                   AmountToAdd))
-    return false;
-
-  auto EndOffset = End.getLValueOffset();
-
-  // The following is a moderately common idiom in C:
-  //
-  // struct Foo { int a; char c[1]; };
-  // struct Foo *F = (struct Foo *)malloc(sizeof(struct Foo) + strlen(Bar));
-  // strcpy(&F->c[0], Bar);
-  //
-  // So, if we see that we're examining a 1-length (or 0-length) array at the
-  // end of a struct with an unknown base, we give up instead of breaking code
-  // that behaves this way. Note that we only do this when Type=1, because
-  // Type=3 is a lower bound, so answering conservatively is fine.
-  if (End.InvalidBase && SubobjectOnly && Type == 1 &&
-      End.Designator.Entries.size() == End.Designator.MostDerivedPathLength &&
-      End.Designator.MostDerivedIsArrayElement &&
-      End.Designator.MostDerivedArraySize < 2 &&
-      isDesignatorAtObjectEnd(Info.Ctx, End))
+  CharUnits EndOffset;
+  if (!determineEndOffset(Info, E, Type, Base, EndOffset))
     return false;
 
   if (BaseOffset > EndOffset)
@@ -6517,7 +6694,7 @@
     case EvalInfo::EM_ConstantFold:
     case EvalInfo::EM_EvaluateForOverflow:
     case EvalInfo::EM_IgnoreSideEffects:
-    case EvalInfo::EM_DesignatorFold:
+    case EvalInfo::EM_OffsetFold:
       // Leave it to IR generation.
       return Error(E);
     case EvalInfo::EM_ConstantExpressionUnevaluated:
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2060,6 +2060,10 @@
   "%0 attribute only applies to pointer arguments">,
   InGroup<IgnoredAttributes>;
 def err_attribute_pointers_only : Error<warn_attribute_pointers_only.Text>;
+def warn_attribute_integers_only : Warning<
+  "%0 attribute only applies to integer arguments">,
+  InGroup<IgnoredAttributes>;
+def err_attribute_integers_only : Error<warn_attribute_integers_only.Text>;
 def warn_attribute_return_pointers_only : Warning<
   "%0 attribute only applies to return values that are pointers">,
   InGroup<IgnoredAttributes>;
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -178,6 +178,41 @@
   }];
 }
 
+def AllocSizeDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+.. Note:: This attribute is not yet fully functional. Specifically, it doesn't
+  yet work with non-const pointers. It is planned to support these in the near
+  future.
+
+The ``alloc_size`` attribute can be placed on functions that return pointers in
+order to hint to the compiler how many bytes of memory will be available at the
+the returned poiner. ``alloc_size`` takes one or two arguments.
+
+- ``alloc_size(N)`` implies that argument number N equals the number of
+  available bytes at the returned pointer.
+- ``alloc_size(N, M)`` implies that the product of argument number N and
+  argument number M equals the number of available bytes at the returned
+  pointer.
+
+Argument numbers are 1-based.
+
+An example of how to use ``alloc_size``
+
+.. code-block:: c
+
+  void *my_malloc(int a) __attribute__((alloc_size(1)));
+  void *my_calloc(int a, int b) __attribute__((alloc_size(1, 2)));
+
+  int main() {
+    void *const p = my_malloc(100);
+    assert(__builtin_object_size(p, 0) == 100);
+    void *const a = my_calloc(20, 5);
+    assert(__builtin_object_size(a, 0) == 100);
+  }
+  }];
+}
+
 def EnableIfDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -709,6 +709,14 @@
   let Documentation = [Undocumented];
 }
 
+def AllocSize : InheritableAttr {
+  let Spellings = [GCC<"alloc_size">];
+  let Subjects = SubjectList<[Function]>;
+  let Args = [IntArgument<"ElemSizeParam">, IntArgument<"NumElemsParam", 1>];
+  let TemplateDependent = 1;
+  let Documentation = [AllocSizeDocs];
+}
+
 def EnableIf : InheritableAttr {
   let Spellings = [GNU<"enable_if">];
   let Subjects = SubjectList<[Function]>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to