]> granicus.if.org Git - clang/commitdiff
Add a spelling of pass_object_size that uses __builtin_dynamic_object_size
authorErik Pilkington <erik.pilkington@gmail.com>
Tue, 19 Mar 2019 20:44:18 +0000 (20:44 +0000)
committerErik Pilkington <erik.pilkington@gmail.com>
Tue, 19 Mar 2019 20:44:18 +0000 (20:44 +0000)
The attribute pass_dynamic_object_size(n) behaves exactly like
pass_object_size(n), but instead of evaluating __builtin_object_size on calls,
it evaluates __builtin_dynamic_object_size, which has the potential to produce
runtime code when the object size can't be determined statically.

Differential revision: https://reviews.llvm.org/D58757

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@356515 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGCall.cpp
lib/Sema/SemaDecl.cpp
test/CodeGen/pass-object-size.c
test/CodeGenCXX/mangle-ms.cpp
test/Sema/pass-object-size.c

index b07835e06d3d4d5c728736af2fe3efd37d0a668d..b6bc7abe49072f1a1daab6cbfb7f2dc234ae0887 100644 (file)
@@ -1560,7 +1560,9 @@ def ReturnsNonNull : InheritableAttr {
 // pass_object_size(N) indicates that the parameter should have
 // __builtin_object_size with Type=N evaluated on the parameter at the callsite.
 def PassObjectSize : InheritableParamAttr {
-  let Spellings = [Clang<"pass_object_size">];
+  let Spellings = [Clang<"pass_object_size">,
+                   Clang<"pass_dynamic_object_size">];
+  let Accessors = [Accessor<"isDynamic", [Clang<"pass_dynamic_object_size">]>];
   let Args = [IntArgument<"Type">];
   let Subjects = SubjectList<[ParmVar]>;
   let Documentation = [PassObjectSizeDocs];
index cd3275bb4e4cf32f2d4d42f6f91c08c0e9bb4cf4..4c87f0c62bed82c9dfe3f220dd588ba31d62f138 100644 (file)
@@ -698,6 +698,15 @@ Currently, ``pass_object_size`` is a bit restricted in terms of its usage:
 * 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 5d5d857c44f2cc3d8aab98e734ffbf0a9efbc169..3357756466fb2e2efc5536ad261895046ca9edf7 100644 (file)
@@ -2833,7 +2833,10 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto,
       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();
+        if (Attr->isDynamic())
+          Out << "U25pass_dynamic_object_size" << Attr->getType();
+        else
+          Out << "U17pass_object_size" << Attr->getType();
       }
     }
   }
index 9112af30666a4271157fdb1e34785dba3f2ed413..c5db936b861c27e3843904b31b12acb25a72409c 100644 (file)
@@ -267,7 +267,7 @@ class MicrosoftCXXNameMangler {
   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(); }
@@ -1761,14 +1761,16 @@ void MicrosoftCXXNameMangler::mangleArgumentType(QualType T,
 void MicrosoftCXXNameMangler::manglePassObjectSizeArg(
     const PassObjectSizeAttr *POSA) {
   int Type = POSA->getType();
+  bool Dynamic = POSA->isDynamic();
 
-  auto Iter = PassObjectSizeArgs.insert(Type).first;
+  auto Iter = PassObjectSizeArgs.insert({Type, Dynamic}).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 =
+        Dynamic ? "__pass_dynamic_object_size" : "__pass_object_size";
+    mangleArtificialTagType(TTK_Enum, Name + llvm::utostr(Type), {"__clang"});
 
     if (TypeBackReferences.size() < 10) {
       size_t Size = TypeBackReferences.size();
index 626578ad84f41b4cb8ceba145ad15373fb6c2f93..73a87a4933d06978f83c093ce85256d747a9a137 100644 (file)
@@ -3452,7 +3452,7 @@ void CodeGenFunction::EmitCallArgs(
     assert(EmittedArg.getScalarVal() && "We emitted nothing for the arg?");
     llvm::Value *V = evaluateOrEmitBuiltinObjectSize(Arg, PS->getType(), T,
                                                      EmittedArg.getScalarVal(),
-                                                     /*IsDynamic=*/false);
+                                                     PS->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 9fa7f48d8d219cca0dff55c377ac787176f8d081..2f8295aa977892b368e62d3c2bde63068bec9188 100644 (file)
@@ -2935,7 +2935,8 @@ static bool hasIdenticalPassObjectSizeAttrs(const FunctionDecl *A,
     const auto *AttrB = B->getAttr<PassObjectSizeAttr>();
     if (AttrA == AttrB)
       return true;
-    return AttrA && AttrB && AttrA->getType() == AttrB->getType();
+    return AttrA && AttrB && AttrA->getType() == AttrB->getType() &&
+           AttrA->isDynamic() == AttrB->isDynamic();
   };
 
   return std::equal(A->param_begin(), A->param_end(), B->param_begin(), AttrEq);
index f5c12317ec32390aabe1f6c5bc5ed055fed4119e..80c0a7519990e500aa87b48977e096e28ebf01b2 100644 (file)
@@ -7,6 +7,7 @@ struct Foo {
 };
 
 #define PS(N) __attribute__((pass_object_size(N)))
+#define PDS(N) __attribute__((pass_dynamic_object_size(N)))
 
 int gi = 0;
 
@@ -16,26 +17,52 @@ int ObjectSize0(void *const p PS(0)) {
   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 @@ void test1() {
   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 @@ int NoViableOverloadObjectSize0(void *const p) __attribute__((overloadable)) {
   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 @@ int NoViableOverloadObjectSize0(void *const p PS(0))
   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 @@ void test3() {
   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 @@ void test5() {
 
   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 @@ int IndirectObjectSize3(void *const p PS(3)) {
   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 @@ void test17(char *C) {
   // 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 0175b961e5e90a7a4a0a8d14ec07d18f84da18e1..75ca3af8250a166ce170c997144454fe4e71b52d 100644 (file)
@@ -457,6 +457,8 @@ int bar(int *const i __attribute__((pass_object_size(1)))) { return 0; }
 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 0745105df831cd93510aea72be959e581bed5ce4..445d20ba6f610ce5812a7029a0010cc1a687d016 100644 (file)
@@ -17,6 +17,9 @@ void h(char *p __attribute__((pass_object_size(0)))) {} //expected-error{{pass_o
 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 (*)(void *)) overloaded;
 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 @@ void FunctionPtrs() {
 
   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}}