erik.pilkington created this revision.
erik.pilkington added reviewers: aaron.ballman, george.burgess.iv, rsmith.
Herald added subscribers: jdoerfert, kristina, dexonsmith, jkorous.
Herald added a project: clang.

This attribute, named `pass_dynamic_object_size` has the same semantics as 
pass_object_size, except that it calls `__builtin_dynamic_object_size` at the 
caller instead of `__builtin_object_size`. You can read more about 
`__builtin_dynamic_object_size` here: 
https://clang.llvm.org/docs/LanguageExtensions.html#evaluating-object-size-dynamically,
 it was introduced in D56760 <https://reviews.llvm.org/D56760>.

rdar://48208787

Thanks for taking a look!
Erik


Repository:
  rC Clang

https://reviews.llvm.org/D58757

Files:
  clang/include/clang/AST/Attr.h
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/lib/AST/ItaniumMangle.cpp
  clang/lib/AST/MicrosoftMangle.cpp
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CGClass.cpp
  clang/lib/CodeGen/CGExpr.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/Sema/SemaChecking.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaLambda.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/CodeGen/pass-object-size.c
  clang/test/CodeGenCXX/mangle-ms.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/pass-object-size.c

Index: clang/test/Sema/pass-object-size.c
===================================================================
--- clang/test/Sema/pass-object-size.c
+++ clang/test/Sema/pass-object-size.c
@@ -17,6 +17,9 @@
 void i(char *p __attribute__((pass_object_size(0)))); // OK -- const is only necessary on definitions, not decls.
 void j(char *p __attribute__((pass_object_size(0), pass_object_size(1)))); //expected-error{{'pass_object_size' attribute can only be applied once per parameter}}
 
+void k(char *p __attribute__((pass_dynamic_object_size))); // expected-error {{'pass_dynamic_object_size' attribute takes one argument}}
+void l(int p __attribute__((pass_dynamic_object_size(0)))); // expected-error {{'pass_dynamic_object_size' attribute only applies to constant pointer arguments}}
+
 #define PS(N) __attribute__((pass_object_size(N)))
 #define overloaded __attribute__((overloadable))
 void Overloaded(void *p PS(0)) overloaded; //expected-note{{previous declaration is here}}
@@ -32,14 +35,17 @@
 void TakeFnOvl(void (*)(int *)) overloaded;
 
 void NotOverloaded(void *p PS(0));
-void IsOverloaded(void *p PS(0)) overloaded;
-void IsOverloaded(char *p) overloaded; // char* inestead of void* is intentional
+void IsOverloaded(void *p PS(0)) overloaded; // expected-note 2 {{candidate address cannot be taken because parameter 1 has pass_object_size attribute}}
+
+// char* inestead of void* is intentional
+void IsOverloaded(char *p) overloaded; // expected-note{{passing argument to parameter 'p' here}} expected-note 2 {{type mismatch}}
+
 void FunctionPtrs() {
   void (*p)(void *) = NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}}
   void (*p2)(void *) = &NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}}
 
-  void (*p3)(void *) = IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type '<overloaded function type>'}} expected-note@-6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-5{{type mismatch}}
-  void (*p4)(void *) = &IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type '<overloaded function type>'}} expected-note@-7{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-6{{type mismatch}}
+  void (*p3)(void *) = IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type '<overloaded function type>'}}
+  void (*p4)(void *) = &IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type '<overloaded function type>'}}
 
   void (*p5)(char *) = IsOverloaded;
   void (*p6)(char *) = &IsOverloaded;
@@ -52,5 +58,11 @@
 
   int P;
   (&NotOverloaded)(&P); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}}
-  (&IsOverloaded)(&P); //expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'char *'}} expected-note@36{{passing argument to parameter 'p' here}}
+  (&IsOverloaded)(&P); //expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'char *'}}
 }
+
+void mismatch(void *p __attribute__((pass_object_size(0)))); // expected-note {{previous declaration is here}}
+void mismatch(void *p __attribute__((pass_dynamic_object_size(0)))); // expected-error {{conflicting pass_object_size attributes on parameters}}
+
+void mismatch2(void *p __attribute__((pass_dynamic_object_size(0)))); // expected-note {{previous declaration is here}}
+void mismatch2(void *p __attribute__((pass_dynamic_object_size(1)))); // expected-error {{conflicting pass_object_size attributes on parameters}}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -117,6 +117,7 @@
 // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method)
 // CHECK-NEXT: Overloadable (SubjectMatchRule_function)
 // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: PassDynamicObjectSize (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
 // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
Index: clang/test/CodeGenCXX/mangle-ms.cpp
===================================================================
--- clang/test/CodeGenCXX/mangle-ms.cpp
+++ clang/test/CodeGenCXX/mangle-ms.cpp
@@ -457,6 +457,8 @@
 int qux(int *const i __attribute__((pass_object_size(1))), int *const j __attribute__((pass_object_size(0)))) { return 0; }
 // CHECK-DAG: define dso_local i32 @"?zot@PassObjectSize@@YAHQAHW4__pass_object_size1@__clang@@01@Z"
 int zot(int *const i __attribute__((pass_object_size(1))), int *const j __attribute__((pass_object_size(1)))) { return 0; }
+// CHECK-DAG: define dso_local i32 @"?silly_word@PassObjectSize@@YAHQAHW4__pass_dynamic_object_size1@__clang@@@Z"
+int silly_word(int *const i __attribute__((pass_dynamic_object_size(1)))) { return 0; }
 }
 
 namespace Atomic {
Index: clang/test/CodeGen/pass-object-size.c
===================================================================
--- clang/test/CodeGen/pass-object-size.c
+++ clang/test/CodeGen/pass-object-size.c
@@ -7,6 +7,7 @@
 };
 
 #define PS(N) __attribute__((pass_object_size(N)))
+#define PDS(N) __attribute__((pass_dynamic_object_size(N)))
 
 int gi = 0;
 
@@ -16,26 +17,52 @@
   return __builtin_object_size(p, 0);
 }
 
+// CHECK-LABEL: define i32 @DynamicObjectSize0(i8* %{{.*}}, i64)
+int DynamicObjectSize0(void *const p PDS(0)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_dynamic_object_size(p, 0);
+}
+
 // CHECK-LABEL: define i32 @ObjectSize1(i8* %{{.*}}, i64)
 int ObjectSize1(void *const p PS(1)) {
   // CHECK-NOT: @llvm.objectsize
   return __builtin_object_size(p, 1);
 }
 
+// CHECK-LABEL: define i32 @DynamicObjectSize1(i8* %{{.*}}, i64)
+int DynamicObjectSize1(void *const p PDS(1)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_dynamic_object_size(p, 1);
+}
+
 // CHECK-LABEL: define i32 @ObjectSize2(i8* %{{.*}}, i64)
 int ObjectSize2(void *const p PS(2)) {
   // CHECK-NOT: @llvm.objectsize
   return __builtin_object_size(p, 2);
 }
 
+// CHECK-LABEL: define i32 @DynamicObjectSize2(i8* %{{.*}}, i64)
+int DynamicObjectSize2(void *const p PDS(2)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 2);
+}
+
 // CHECK-LABEL: define i32 @ObjectSize3(i8* %{{.*}}, i64)
 int ObjectSize3(void *const p PS(3)) {
   // CHECK-NOT: @llvm.objectsize
   return __builtin_object_size(p, 3);
 }
 
+// CHECK-LABEL: define i32 @DynamicObjectSize3(i8* %{{.*}}, i64)
+int DynamicObjectSize3(void *const p PDS(3)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 3);
+}
+
+void *malloc(unsigned long) __attribute__((alloc_size(1)));
+
 // CHECK-LABEL: define void @test1
-void test1() {
+void test1(unsigned long sz) {
   struct Foo t[10];
 
   // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 360)
@@ -55,6 +82,21 @@
   gi = ObjectSize2(&t[1].t[1]);
   // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36)
   gi = ObjectSize3(&t[1].t[1]);
+
+  char *ptr = (char *)malloc(sz);
+
+  // CHECK: [[REG:%.*]] = call i64 @llvm.objectsize.i64.p0i8({{.*}}, i1 false, i1 true, i1 true)
+  // CHECK: call i32 @DynamicObjectSize0(i8* %{{.*}}, i64 [[REG]])
+  gi = DynamicObjectSize0(ptr);
+
+  // CHECK: [[WITH_OFFSET:%.*]] = getelementptr
+  // CHECK: [[REG:%.*]] = call i64 @llvm.objectsize.i64.p0i8(i8* [[WITH_OFFSET]], i1 false, i1 true, i1 true)
+  // CHECK: call i32 @DynamicObjectSize0(i8* {{.*}}, i64 [[REG]])
+  gi = DynamicObjectSize0(ptr+10);
+
+  // CHECK: [[REG:%.*]] = call i64 @llvm.objectsize.i64.p0i8({{.*}}, i1 true, i1 true, i1 true)
+  // CHECK: call i32 @DynamicObjectSize2(i8* {{.*}}, i64 [[REG]])
+  gi = DynamicObjectSize2(ptr);
 }
 
 // CHECK-LABEL: define void @test2
@@ -72,6 +114,13 @@
   return __builtin_object_size(p, 0);
 }
 
+// CHECK-LABEL: define i32 @_Z34NoViableOverloadDynamicObjectSize0Pv
+int NoViableOverloadDynamicObjectSize0(void *const p)
+  __attribute__((overloadable)) {
+  // CHECK: @llvm.objectsize
+  return __builtin_object_size(p, 0);
+}
+
 // CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize1Pv
 int NoViableOverloadObjectSize1(void *const p) __attribute__((overloadable)) {
   // CHECK: @llvm.objectsize
@@ -97,6 +146,11 @@
   return __builtin_object_size(p, 0);
 }
 
+int NoViableOverloadDynamicObjectSize0(void *const p PDS(0))
+  __attribute__((overloadable)) {
+  return __builtin_dynamic_object_size(p, 0);
+}
+
 int NoViableOverloadObjectSize1(void *const p PS(1))
     __attribute__((overloadable)) {
   return __builtin_object_size(p, 1);
@@ -154,6 +208,9 @@
   gi = NoViableOverloadObjectSize2(&t[1].t[1]);
   // CHECK: call i32 @_Z27NoViableOverloadObjectSize3PvU17pass_object_size3(i8* %{{.*}}, i64 36)
   gi = NoViableOverloadObjectSize3(&t[1].t[1]);
+
+  // CHECK: call i32 @_Z34NoViableOverloadDynamicObjectSize0PvU25pass_dynamic_object_size0(i8* %{{.*}}, i64 360)
+  gi = NoViableOverloadDynamicObjectSize0(&t[1]);
 }
 
 // CHECK-LABEL: define void @test4
@@ -183,6 +240,9 @@
 
   int (*f)(void *) = &NoViableOverloadObjectSize0;
   gi = f(&t[1]);
+
+  int (*g)(void *) = &NoViableOverloadDynamicObjectSize0;
+  gi = g(&t[1]);
 }
 
 // CHECK-LABEL: define i32 @IndirectObjectSize0
@@ -213,6 +273,12 @@
   return ObjectSize3(p);
 }
 
+int IndirectDynamicObjectSize0(void *const p PDS(0)) {
+  // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 %{{.*}})
+  // CHECK-NOT: @llvm.objectsize
+  return ObjectSize0(p);
+}
+
 int Overload0(void *, size_t, void *, size_t);
 int OverloadNoSize(void *, void *);
 
@@ -418,3 +484,10 @@
   // CHECK: call i32 @ObjectSize0(i8* [[PTR]]
   ObjectSize0(C + ({ int a = 65535; a; }));
 }
+
+// CHECK-LABEL: define void @test18
+void test18(char *const p PDS(0)) {
+  // CHECK-NOT: llvm.objectsize
+  gi = __builtin_dynamic_object_size(p, 0);
+  gi = __builtin_object_size(p, 0);
+}
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -4804,7 +4804,7 @@
             HasAnyInterestingExtParameterInfos = true;
           }
 
-          if (Param->hasAttr<PassObjectSizeAttr>()) {
+          if (getPassObjectSizeInfo(Param) != None) {
             ExtParameterInfos[i] = ExtParameterInfos[i].withHasPassObjectSize();
             HasAnyInterestingExtParameterInfos = true;
           }
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -40,7 +40,7 @@
 
 static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) {
   return llvm::any_of(FD->parameters(), [](const ParmVarDecl *P) {
-    return P->hasAttr<PassObjectSizeAttr>();
+    return getPassObjectSizeInfo(P) != None;
   });
 }
 
@@ -9583,7 +9583,7 @@
   }
 
   auto I = llvm::find_if(FD->parameters(), [](const ParmVarDecl *P) {
-    return P->hasAttr<PassObjectSizeAttr>();
+    return getPassObjectSizeInfo(P) != None;
   });
   if (I == FD->param_end())
     return true;
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1213,7 +1213,7 @@
   // This conversion is explicitly disabled if the lambda's function has
   // pass_object_size attributes on any of its parameters.
   auto HasPassObjectSizeAttr = [](const ParmVarDecl *P) {
-    return P->hasAttr<PassObjectSizeAttr>();
+    return getPassObjectSizeInfo(P) != None;
   };
   if (llvm::any_of(CallOperator->parameters(), HasPassObjectSizeAttr))
     return;
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -1104,9 +1104,13 @@
       cast<NamedDecl>(D), AL.getAttributeSpellingListIndex()));
 }
 
-static void handlePassObjectSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
-  if (D->hasAttr<PassObjectSizeAttr>()) {
-    S.Diag(D->getBeginLoc(), diag::err_attribute_only_once_per_parameter) << AL;
+static void handlePassObjectSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL,
+                                     bool IsDynamic) {
+  assert(isa<ParmVarDecl>(D) && "Should be checked already!");
+  auto *PVD = cast<ParmVarDecl>(D);
+  if (getPassObjectSizeInfo(PVD) != None) {
+    S.Diag(PVD->getBeginLoc(), diag::err_attribute_only_once_per_parameter)
+        << AL;
     return;
   }
 
@@ -1128,13 +1132,20 @@
   // kindness to users, we allow the parameter to be non-const for declarations.
   // At this point, we have no clue if `D` belongs to a function declaration or
   // definition, so we defer the constness check until later.
-  if (!cast<ParmVarDecl>(D)->getType()->isPointerType()) {
+  if (!PVD->getType()->isPointerType()) {
     S.Diag(D->getBeginLoc(), diag::err_attribute_pointers_only) << AL << 1;
     return;
   }
 
-  D->addAttr(::new (S.Context) PassObjectSizeAttr(
-      AL.getRange(), S.Context, (int)Type, AL.getAttributeSpellingListIndex()));
+  if (IsDynamic) {
+    D->addAttr(::new (S.Context) PassDynamicObjectSizeAttr(
+        AL.getRange(), S.Context, (int)Type,
+        AL.getAttributeSpellingListIndex()));
+  } else {
+    D->addAttr(::new (S.Context)
+                   PassObjectSizeAttr(AL.getRange(), S.Context, (int)Type,
+                                      AL.getAttributeSpellingListIndex()));
+  }
 }
 
 static void handleConsumableAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
@@ -6722,7 +6733,10 @@
     handleConstantAttr(S, D, AL);
     break;
   case ParsedAttr::AT_PassObjectSize:
-    handlePassObjectSizeAttr(S, D, AL);
+    handlePassObjectSizeAttr(S, D, AL, false);
+    break;
+  case ParsedAttr::AT_PassDynamicObjectSize:
+    handlePassObjectSizeAttr(S, D, AL, true);
     break;
   case ParsedAttr::AT_Constructor:
     handleConstructorAttr(S, D, AL);
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -2931,11 +2931,12 @@
   assert(A->getNumParams() == B->getNumParams());
 
   auto AttrEq = [](const ParmVarDecl *A, const ParmVarDecl *B) {
-    const auto *AttrA = A->getAttr<PassObjectSizeAttr>();
-    const auto *AttrB = B->getAttr<PassObjectSizeAttr>();
-    if (AttrA == AttrB)
-      return true;
-    return AttrA && AttrB && AttrA->getType() == AttrB->getType();
+    Optional<PassObjectSizeInfo> InfoA = getPassObjectSizeInfo(A);
+    Optional<PassObjectSizeInfo> InfoB = getPassObjectSizeInfo(B);
+    if (!InfoA || !InfoB)
+      return !InfoA && !InfoB;
+    return std::tie(InfoA->Type, InfoA->IsDynamic) ==
+           std::tie(InfoB->Type, InfoB->IsDynamic);
   };
 
   return std::equal(A->param_begin(), A->param_end(), B->param_begin(), AttrEq);
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -12272,10 +12272,10 @@
     // constant at function definitions. Because we lack information about
     // whether we're on a declaration or definition when we're instantiating the
     // attribute, we need to check for constness here.
-    if (const auto *Attr = Param->getAttr<PassObjectSizeAttr>())
+    if (Optional<PassObjectSizeInfo> Info = getPassObjectSizeInfo(Param))
       if (!Param->getType().isConstQualified())
         Diag(Param->getLocation(), diag::err_attribute_pointers_only)
-            << Attr->getSpelling() << 1;
+            << Info->TheAttr->getSpelling() << 1;
 
     // Check for parameter names shadowing fields from the class.
     if (LangOpts.CPlusPlus && !Param->isInvalidDecl()) {
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1273,7 +1273,7 @@
   if (PassedParams) {
     for (auto *Param : FD->parameters()) {
       Args.push_back(Param);
-      if (!Param->hasAttr<PassObjectSizeAttr>())
+      if (getPassObjectSizeInfo(Param) == None)
         continue;
 
       auto *Implicit = ImplicitParamDecl::Create(
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -898,13 +898,12 @@
   if (!ParamDecl)
     return nullptr;
 
-  auto *POSAttr = ParamDecl->getAttr<PassObjectSizeAttr>();
-  if (!POSAttr)
+  Optional<PassObjectSizeInfo> Info = getPassObjectSizeInfo(ParamDecl);
+  if (!Info)
     return nullptr;
 
   // Don't load the size if it's a lower bound.
-  int POSType = POSAttr->getType();
-  if (POSType != 0 && POSType != 1)
+  if (Info->Type != 0 && Info->Type != 1)
     return nullptr;
 
   // Find the implicit size parameter.
Index: clang/lib/CodeGen/CGClass.cpp
===================================================================
--- clang/lib/CodeGen/CGClass.cpp
+++ clang/lib/CodeGen/CGClass.cpp
@@ -2188,7 +2188,7 @@
       EmitDelegateCallArg(Args, Param, E->getLocation());
 
       // Forward __attribute__(pass_object_size).
-      if (Param->hasAttr<PassObjectSizeAttr>()) {
+      if (getPassObjectSizeInfo(Param) != None) {
         auto *POSParam = SizeArguments[Param];
         assert(POSParam && "missing pass_object_size value for forwarding");
         EmitDelegateCallArg(Args, POSParam, E->getLocation());
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -3442,17 +3442,19 @@
                                          RValue EmittedArg) {
     if (!AC.hasFunctionDecl() || I >= AC.getNumParams())
       return;
-    auto *PS = AC.getParamDecl(I)->getAttr<PassObjectSizeAttr>();
-    if (PS == nullptr)
+
+    Optional<PassObjectSizeInfo> Info =
+        getPassObjectSizeInfo(AC.getParamDecl(I));
+    if (!Info)
       return;
 
     const auto &Context = getContext();
     auto SizeTy = Context.getSizeType();
     auto T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
     assert(EmittedArg.getScalarVal() && "We emitted nothing for the arg?");
-    llvm::Value *V = evaluateOrEmitBuiltinObjectSize(Arg, PS->getType(), T,
+    llvm::Value *V = evaluateOrEmitBuiltinObjectSize(Arg, Info->Type, T,
                                                      EmittedArg.getScalarVal(),
-                                                     /*IsDynamic=*/false);
+                                                     Info->IsDynamic);
     Args.add(RValue::get(V), SizeTy);
     // If we're emitting args in reverse, be sure to do so with
     // pass_object_size, as well.
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -518,19 +518,19 @@
   // We need to reference an argument if the pointer is a parameter with the
   // pass_object_size attribute.
   if (auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) {
-    auto *Param = dyn_cast<ParmVarDecl>(D->getDecl());
-    auto *PS = D->getDecl()->getAttr<PassObjectSizeAttr>();
-    if (Param != nullptr && PS != nullptr &&
-        areBOSTypesCompatible(PS->getType(), Type)) {
-      auto Iter = SizeArguments.find(Param);
-      assert(Iter != SizeArguments.end());
-
-      const ImplicitParamDecl *D = Iter->second;
-      auto DIter = LocalDeclMap.find(D);
-      assert(DIter != LocalDeclMap.end());
-
-      return EmitLoadOfScalar(DIter->second, /*volatile=*/false,
-                              getContext().getSizeType(), E->getBeginLoc());
+    if (auto *Param = dyn_cast<ParmVarDecl>(D->getDecl())) {
+      Optional<PassObjectSizeInfo> Info = getPassObjectSizeInfo(Param);
+      if (Info && areBOSTypesCompatible(Info->Type, Type)) {
+        auto Iter = SizeArguments.find(Param);
+        assert(Iter != SizeArguments.end());
+
+        const ImplicitParamDecl *D = Iter->second;
+        auto DIter = LocalDeclMap.find(D);
+        assert(DIter != LocalDeclMap.end());
+
+        return EmitLoadOfScalar(DIter->second, /*volatile=*/false,
+                                getContext().getSizeType(), E->getBeginLoc());
+      }
     }
   }
 
Index: clang/lib/AST/MicrosoftMangle.cpp
===================================================================
--- clang/lib/AST/MicrosoftMangle.cpp
+++ clang/lib/AST/MicrosoftMangle.cpp
@@ -267,7 +267,7 @@
   typedef llvm::DenseMap<const void *, unsigned> ArgBackRefMap;
   ArgBackRefMap TypeBackReferences;
 
-  typedef std::set<int> PassObjectSizeArgsSet;
+  typedef std::set<std::pair<int, bool>> PassObjectSizeArgsSet;
   PassObjectSizeArgsSet PassObjectSizeArgs;
 
   ASTContext &getASTContext() const { return Context.getASTContext(); }
@@ -344,7 +344,7 @@
   void mangleObjCMethodName(const ObjCMethodDecl *MD);
 
   void mangleArgumentType(QualType T, SourceRange Range);
-  void manglePassObjectSizeArg(const PassObjectSizeAttr *POSA);
+  void manglePassObjectSizeArg(PassObjectSizeInfo Info);
 
   bool isArtificialTagType(QualType T) const;
 
@@ -1758,17 +1758,16 @@
   }
 }
 
-void MicrosoftCXXNameMangler::manglePassObjectSizeArg(
-    const PassObjectSizeAttr *POSA) {
-  int Type = POSA->getType();
-
-  auto Iter = PassObjectSizeArgs.insert(Type).first;
+void MicrosoftCXXNameMangler::manglePassObjectSizeArg(PassObjectSizeInfo Info) {
+  auto Iter = PassObjectSizeArgs.insert({Info.Type, Info.IsDynamic}).first;
   auto *TypePtr = (const void *)&*Iter;
   ArgBackRefMap::iterator Found = TypeBackReferences.find(TypePtr);
 
   if (Found == TypeBackReferences.end()) {
-    mangleArtificialTagType(TTK_Enum, "__pass_object_size" + llvm::utostr(Type),
-                           {"__clang"});
+    std::string Name =
+        Info.IsDynamic ? "__pass_dynamic_object_size" : "__pass_object_size";
+    mangleArtificialTagType(TTK_Enum, Name + llvm::utostr(Info.Type),
+                            {"__clang"});
 
     if (TypeBackReferences.size() < 10) {
       size_t Size = TypeBackReferences.size();
@@ -2254,9 +2253,11 @@
       // FIXME: Is there a defined extension notation for the MS ABI, or is it
       // necessary to just cross our fingers and hope this type+namespace
       // combination doesn't conflict with anything?
-      if (D)
-        if (const auto *P = D->getParamDecl(I)->getAttr<PassObjectSizeAttr>())
-          manglePassObjectSizeArg(P);
+      if (D) {
+        const ParmVarDecl *PVD = D->getParamDecl(I);
+        if (Optional<PassObjectSizeInfo> Info = getPassObjectSizeInfo(PVD))
+          manglePassObjectSizeArg(*Info);
+      }
     }
     // <builtin-type>      ::= Z  # ellipsis
     if (Proto->isVariadic())
Index: clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- clang/lib/AST/ItaniumMangle.cpp
+++ clang/lib/AST/ItaniumMangle.cpp
@@ -2830,10 +2830,16 @@
     mangleType(Context.getASTContext().getSignatureParameterType(ParamTy));
 
     if (FD) {
-      if (auto *Attr = FD->getParamDecl(I)->getAttr<PassObjectSizeAttr>()) {
-        // Attr can only take 1 character, so we can hardcode the length below.
-        assert(Attr->getType() <= 9 && Attr->getType() >= 0);
-        Out << "U17pass_object_size" << Attr->getType();
+      // FIXME: It would be nice to move this before the type, so it conforms to
+      // the production:
+      //   <type> ::= U <source-name> <type>	# vendor extended type qualifier
+      const ParmVarDecl *PVD = FD->getParamDecl(I);
+      if (auto *POS = PVD->getAttr<PassObjectSizeAttr>()) {
+        assert(POS->getType() <= 9 && POS->getType() >= 0);
+        Out << "U17pass_object_size" << POS->getType();
+      } else if (auto *PDOS = PVD->getAttr<PassDynamicObjectSizeAttr>()) {
+        assert(PDOS->getType() <= 9 && PDOS->getType() >= 0);
+        Out << "U25pass_dynamic_object_size" << PDOS->getType();
       }
     }
   }
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -698,6 +698,15 @@
 * It is an error to apply the ``pass_object_size`` attribute to parameters that
   are not pointers. Additionally, any parameter that ``pass_object_size`` is
   applied to must be marked ``const`` at its function's definition.
+
+Clang also supports the ``pass_dynamic_object_size`` attribute, which behaves
+identically to ``pass_object_size``, but evaluates a call to
+``__builtin_dynamic_object_size`` at the callee instead of
+``__builtin_object_size``. ``__builtin_dynamic_object_size`` provides some extra
+runtime checks when the object size can't be determined at compile-time. You can
+read more about ``__builtin_dynamic_object_size`` `here
+<https://clang.llvm.org/docs/LanguageExtensions.html#evaluating-object-size-dynamically>`_.
+
   }];
 }
 
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1566,6 +1566,13 @@
   let Documentation = [PassObjectSizeDocs];
 }
 
+def PassDynamicObjectSize : InheritableParamAttr {
+  let Spellings = [Clang<"pass_dynamic_object_size">];
+  let Args = [IntArgument<"Type">];
+  let Subjects = SubjectList<[ParmVar]>;
+  let Documentation = [PassObjectSizeDocs];
+}
+
 def FortifyStdLib : InheritableAttr {
   let Spellings = [Clang<"fortify_stdlib">];
   let Args = [IntArgument<"Type">, IntArgument<"Flag">];
Index: clang/include/clang/AST/Attr.h
===================================================================
--- clang/include/clang/AST/Attr.h
+++ clang/include/clang/AST/Attr.h
@@ -345,6 +345,24 @@
                   DiagnosticsEngine::ak_attr);
   return PD;
 }
+
+/// Quick wrapper around either a pass_object_size or pass_dynamic_object_size
+/// attribute. These have a very similar interface, but are still distinct
+/// attributes.
+struct PassObjectSizeInfo {
+  Attr *TheAttr;
+  int Type;
+  bool IsDynamic;
+};
+
+inline Optional<PassObjectSizeInfo>
+getPassObjectSizeInfo(const ParmVarDecl *PVD) {
+  if (auto *POS = PVD->getAttr<PassObjectSizeAttr>())
+    return PassObjectSizeInfo{POS, POS->getType(), false};
+  else if (auto *PDOS = PVD->getAttr<PassDynamicObjectSizeAttr>())
+    return PassObjectSizeInfo{PDOS, PDOS->getType(), true};
+  return None;
+}
 }  // end namespace clang
 
 #endif
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to