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
This commit is contained in:
Erik Pilkington 2019-03-19 20:44:18 +00:00
parent 1663c9466f
commit 02d5fb1a6e
9 changed files with 118 additions and 14 deletions

View 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];

View 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>`_.
}];
}

View 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();
}
}
}

View 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();

View 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.

View 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);

View 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);
}

View 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 {

View 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}}