// 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];
* 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>`_.
+
}];
}
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();
}
}
}
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(); }
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();
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.
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);
};
#define PS(N) __attribute__((pass_object_size(N)))
+#define PDS(N) __attribute__((pass_dynamic_object_size(N)))
int gi = 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)
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
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
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);
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
int (*f)(void *) = &NoViableOverloadObjectSize0;
gi = f(&t[1]);
+
+ int (*g)(void *) = &NoViableOverloadDynamicObjectSize0;
+ gi = g(&t[1]);
}
// CHECK-LABEL: define i32 @IndirectObjectSize0
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 *);
// 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);
+}
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 {
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}}
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;
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}}