From 02d5fb1a6efb96706ca781c6a50624d0e0d7b842 Mon Sep 17 00:00:00 2001 From: Erik Pilkington Date: Tue, 19 Mar 2019 20:44:18 +0000 Subject: [PATCH] Add a spelling of pass_object_size that uses __builtin_dynamic_object_size 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 llvm-svn: 356515 --- clang/include/clang/Basic/Attr.td | 4 +- clang/include/clang/Basic/AttrDocs.td | 9 ++++ clang/lib/AST/ItaniumMangle.cpp | 5 +- clang/lib/AST/MicrosoftMangle.cpp | 10 ++-- clang/lib/CodeGen/CGCall.cpp | 2 +- clang/lib/Sema/SemaDecl.cpp | 3 +- clang/test/CodeGen/pass-object-size.c | 75 ++++++++++++++++++++++++++- clang/test/CodeGenCXX/mangle-ms.cpp | 2 + clang/test/Sema/pass-object-size.c | 22 ++++++-- 9 files changed, 118 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b07835e06d3d..b6bc7abe4907 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index cd3275bb4e4c..4c87f0c62bed 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -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 +`_. + }]; } diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 5d5d857c44f2..3357756466fb 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2833,7 +2833,10 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto, if (auto *Attr = FD->getParamDecl(I)->getAttr()) { // 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(); } } } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 9112af30666a..c5db936b861c 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -267,7 +267,7 @@ class MicrosoftCXXNameMangler { typedef llvm::DenseMap ArgBackRefMap; ArgBackRefMap TypeBackReferences; - typedef std::set PassObjectSizeArgsSet; + typedef std::set> 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(); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 626578ad84f4..73a87a4933d0 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -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. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9fa7f48d8d21..2f8295aa9778 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2935,7 +2935,8 @@ static bool hasIdenticalPassObjectSizeAttrs(const FunctionDecl *A, const auto *AttrB = B->getAttr(); 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); diff --git a/clang/test/CodeGen/pass-object-size.c b/clang/test/CodeGen/pass-object-size.c index f5c12317ec32..80c0a7519990 100644 --- a/clang/test/CodeGen/pass-object-size.c +++ b/clang/test/CodeGen/pass-object-size.c @@ -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); +} diff --git a/clang/test/CodeGenCXX/mangle-ms.cpp b/clang/test/CodeGenCXX/mangle-ms.cpp index 0175b961e5e9..75ca3af8250a 100644 --- a/clang/test/CodeGenCXX/mangle-ms.cpp +++ b/clang/test/CodeGenCXX/mangle-ms.cpp @@ -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 { diff --git a/clang/test/Sema/pass-object-size.c b/clang/test/Sema/pass-object-size.c index 0745105df831..445d20ba6f61 100644 --- a/clang/test/Sema/pass-object-size.c +++ b/clang/test/Sema/pass-object-size.c @@ -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 ''}} 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 ''}} 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 ''}} + void (*p4)(void *) = &IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of 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}}