forked from OSchip/llvm-project
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:
parent
1663c9466f
commit
02d5fb1a6e
|
@ -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];
|
||||
|
|
|
@ -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>`_.
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}}
|
||||
|
|
Loading…
Reference in New Issue