mirror of https://github.com/microsoft/clang.git
[CodeGen] Don't reemit expressions for pass_object_size params.
This fixes an assertion failure in cases where we had expression statements that declared variables nested inside of pass_object_size args. Since we were emitting the same ExprStmt twice (once for the arg, once for the @llvm.objectsize call), we were getting issues with redefining locals. This also means that we can be more lax about when we emit @llvm.objectsize for pass_object_size args: since we're reusing the arg's value itself, we don't have to care so much about side-effects. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@295935 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
b8bfb64f1c
commit
e604e8210f
|
@ -420,10 +420,11 @@ getDefaultBuiltinObjectSizeResult(unsigned Type, llvm::IntegerType *ResType) {
|
||||||
|
|
||||||
llvm::Value *
|
llvm::Value *
|
||||||
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
|
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||||
llvm::IntegerType *ResType) {
|
llvm::IntegerType *ResType,
|
||||||
|
llvm::Value *EmittedE) {
|
||||||
uint64_t ObjectSize;
|
uint64_t ObjectSize;
|
||||||
if (!E->tryEvaluateObjectSize(ObjectSize, getContext(), Type))
|
if (!E->tryEvaluateObjectSize(ObjectSize, getContext(), Type))
|
||||||
return emitBuiltinObjectSize(E, Type, ResType);
|
return emitBuiltinObjectSize(E, Type, ResType, EmittedE);
|
||||||
return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
|
return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,9 +433,14 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||||
/// - A llvm::Argument (if E is a param with the pass_object_size attribute on
|
/// - A llvm::Argument (if E is a param with the pass_object_size attribute on
|
||||||
/// it)
|
/// it)
|
||||||
/// - A call to the @llvm.objectsize intrinsic
|
/// - A call to the @llvm.objectsize intrinsic
|
||||||
|
///
|
||||||
|
/// EmittedE is the result of emitting `E` as a scalar expr. If it's non-null
|
||||||
|
/// and we wouldn't otherwise try to reference a pass_object_size parameter,
|
||||||
|
/// we'll call @llvm.objectsize on EmittedE, rather than emitting E.
|
||||||
llvm::Value *
|
llvm::Value *
|
||||||
CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||||
llvm::IntegerType *ResType) {
|
llvm::IntegerType *ResType,
|
||||||
|
llvm::Value *EmittedE) {
|
||||||
// We need to reference an argument if the pointer is a parameter with the
|
// We need to reference an argument if the pointer is a parameter with the
|
||||||
// pass_object_size attribute.
|
// pass_object_size attribute.
|
||||||
if (auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) {
|
if (auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) {
|
||||||
|
@ -457,10 +463,10 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||||
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
|
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
|
||||||
// evaluate E for side-effects. In either case, we shouldn't lower to
|
// evaluate E for side-effects. In either case, we shouldn't lower to
|
||||||
// @llvm.objectsize.
|
// @llvm.objectsize.
|
||||||
if (Type == 3 || E->HasSideEffects(getContext()))
|
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
|
||||||
return getDefaultBuiltinObjectSizeResult(Type, ResType);
|
return getDefaultBuiltinObjectSizeResult(Type, ResType);
|
||||||
|
|
||||||
Value *Ptr = EmitScalarExpr(E);
|
Value *Ptr = EmittedE ? EmittedE : EmitScalarExpr(E);
|
||||||
assert(Ptr->getType()->isPointerTy() &&
|
assert(Ptr->getType()->isPointerTy() &&
|
||||||
"Non-pointer passed to __builtin_object_size?");
|
"Non-pointer passed to __builtin_object_size?");
|
||||||
|
|
||||||
|
@ -965,7 +971,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
||||||
|
|
||||||
// We pass this builtin onto the optimizer so that it can figure out the
|
// We pass this builtin onto the optimizer so that it can figure out the
|
||||||
// object size in more complex cases.
|
// object size in more complex cases.
|
||||||
return RValue::get(emitBuiltinObjectSize(E->getArg(0), Type, ResType));
|
return RValue::get(emitBuiltinObjectSize(E->getArg(0), Type, ResType,
|
||||||
|
/*EmittedE=*/nullptr));
|
||||||
}
|
}
|
||||||
case Builtin::BI__builtin_prefetch: {
|
case Builtin::BI__builtin_prefetch: {
|
||||||
Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
|
Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
|
||||||
|
|
|
@ -3243,20 +3243,6 @@ void CodeGenFunction::EmitCallArgs(
|
||||||
EvaluationOrder Order) {
|
EvaluationOrder Order) {
|
||||||
assert((int)ArgTypes.size() == (ArgRange.end() - ArgRange.begin()));
|
assert((int)ArgTypes.size() == (ArgRange.end() - ArgRange.begin()));
|
||||||
|
|
||||||
auto MaybeEmitImplicitObjectSize = [&](unsigned I, const Expr *Arg) {
|
|
||||||
if (CalleeDecl == nullptr || I >= CalleeDecl->getNumParams())
|
|
||||||
return;
|
|
||||||
auto *PS = CalleeDecl->getParamDecl(I)->getAttr<PassObjectSizeAttr>();
|
|
||||||
if (PS == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto &Context = getContext();
|
|
||||||
auto SizeTy = Context.getSizeType();
|
|
||||||
auto T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
|
|
||||||
llvm::Value *V = evaluateOrEmitBuiltinObjectSize(Arg, PS->getType(), T);
|
|
||||||
Args.add(RValue::get(V), SizeTy);
|
|
||||||
};
|
|
||||||
|
|
||||||
// We *have* to evaluate arguments from right to left in the MS C++ ABI,
|
// We *have* to evaluate arguments from right to left in the MS C++ ABI,
|
||||||
// because arguments are destroyed left to right in the callee. As a special
|
// because arguments are destroyed left to right in the callee. As a special
|
||||||
// case, there are certain language constructs that require left-to-right
|
// case, there are certain language constructs that require left-to-right
|
||||||
|
@ -3267,6 +3253,27 @@ void CodeGenFunction::EmitCallArgs(
|
||||||
? Order == EvaluationOrder::ForceLeftToRight
|
? Order == EvaluationOrder::ForceLeftToRight
|
||||||
: Order != EvaluationOrder::ForceRightToLeft;
|
: Order != EvaluationOrder::ForceRightToLeft;
|
||||||
|
|
||||||
|
auto MaybeEmitImplicitObjectSize = [&](unsigned I, const Expr *Arg,
|
||||||
|
RValue EmittedArg) {
|
||||||
|
if (CalleeDecl == nullptr || I >= CalleeDecl->getNumParams())
|
||||||
|
return;
|
||||||
|
auto *PS = CalleeDecl->getParamDecl(I)->getAttr<PassObjectSizeAttr>();
|
||||||
|
if (PS == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto &Context = getContext();
|
||||||
|
auto SizeTy = Context.getSizeType();
|
||||||
|
auto T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
|
||||||
|
assert(EmittedArg.getScalarVal() && "We emitted nothing for the arg?");
|
||||||
|
llvm::Value *V = evaluateOrEmitBuiltinObjectSize(Arg, PS->getType(), T,
|
||||||
|
EmittedArg.getScalarVal());
|
||||||
|
Args.add(RValue::get(V), SizeTy);
|
||||||
|
// If we're emitting args in reverse, be sure to do so with
|
||||||
|
// pass_object_size, as well.
|
||||||
|
if (!LeftToRight)
|
||||||
|
std::swap(Args.back(), *(&Args.back() - 1));
|
||||||
|
};
|
||||||
|
|
||||||
// Insert a stack save if we're going to need any inalloca args.
|
// Insert a stack save if we're going to need any inalloca args.
|
||||||
bool HasInAllocaArgs = false;
|
bool HasInAllocaArgs = false;
|
||||||
if (CGM.getTarget().getCXXABI().isMicrosoft()) {
|
if (CGM.getTarget().getCXXABI().isMicrosoft()) {
|
||||||
|
@ -3284,11 +3291,20 @@ void CodeGenFunction::EmitCallArgs(
|
||||||
for (unsigned I = 0, E = ArgTypes.size(); I != E; ++I) {
|
for (unsigned I = 0, E = ArgTypes.size(); I != E; ++I) {
|
||||||
unsigned Idx = LeftToRight ? I : E - I - 1;
|
unsigned Idx = LeftToRight ? I : E - I - 1;
|
||||||
CallExpr::const_arg_iterator Arg = ArgRange.begin() + Idx;
|
CallExpr::const_arg_iterator Arg = ArgRange.begin() + Idx;
|
||||||
if (!LeftToRight) MaybeEmitImplicitObjectSize(Idx, *Arg);
|
unsigned InitialArgSize = Args.size();
|
||||||
EmitCallArg(Args, *Arg, ArgTypes[Idx]);
|
EmitCallArg(Args, *Arg, ArgTypes[Idx]);
|
||||||
EmitNonNullArgCheck(Args.back().RV, ArgTypes[Idx], (*Arg)->getExprLoc(),
|
// In particular, we depend on it being the last arg in Args, and the
|
||||||
CalleeDecl, ParamsToSkip + Idx);
|
// objectsize bits depend on there only being one arg if !LeftToRight.
|
||||||
if (LeftToRight) MaybeEmitImplicitObjectSize(Idx, *Arg);
|
assert(InitialArgSize + 1 == Args.size() &&
|
||||||
|
"The code below depends on only adding one arg per EmitCallArg");
|
||||||
|
(void)InitialArgSize;
|
||||||
|
RValue RVArg = Args.back().RV;
|
||||||
|
EmitNonNullArgCheck(RVArg, ArgTypes[Idx], (*Arg)->getExprLoc(), CalleeDecl,
|
||||||
|
ParamsToSkip + Idx);
|
||||||
|
// @llvm.objectsize should never have side-effects and shouldn't need
|
||||||
|
// destruction/cleanups, so we can safely "emit" it after its arg,
|
||||||
|
// regardless of right-to-leftness
|
||||||
|
MaybeEmitImplicitObjectSize(Idx, *Arg, RVArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LeftToRight) {
|
if (!LeftToRight) {
|
||||||
|
|
|
@ -3510,14 +3510,18 @@ private:
|
||||||
/// \brief Attempts to statically evaluate the object size of E. If that
|
/// \brief Attempts to statically evaluate the object size of E. If that
|
||||||
/// fails, emits code to figure the size of E out for us. This is
|
/// fails, emits code to figure the size of E out for us. This is
|
||||||
/// pass_object_size aware.
|
/// pass_object_size aware.
|
||||||
|
///
|
||||||
|
/// If EmittedExpr is non-null, this will use that instead of re-emitting E.
|
||||||
llvm::Value *evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
|
llvm::Value *evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||||
llvm::IntegerType *ResType);
|
llvm::IntegerType *ResType,
|
||||||
|
llvm::Value *EmittedE);
|
||||||
|
|
||||||
/// \brief Emits the size of E, as required by __builtin_object_size. This
|
/// \brief Emits the size of E, as required by __builtin_object_size. This
|
||||||
/// function is aware of pass_object_size parameters, and will act accordingly
|
/// function is aware of pass_object_size parameters, and will act accordingly
|
||||||
/// if E is a parameter with the pass_object_size attribute.
|
/// if E is a parameter with the pass_object_size attribute.
|
||||||
llvm::Value *emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
llvm::Value *emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||||
llvm::IntegerType *ResType);
|
llvm::IntegerType *ResType,
|
||||||
|
llvm::Value *EmittedE);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
|
@ -343,16 +343,26 @@ void test12(void *const p __attribute__((pass_object_size(3)))) {
|
||||||
|
|
||||||
// CHECK-LABEL: define void @test13
|
// CHECK-LABEL: define void @test13
|
||||||
void test13() {
|
void test13() {
|
||||||
// Ensuring that we don't lower objectsize if the expression has side-effects
|
|
||||||
char c[10];
|
char c[10];
|
||||||
|
unsigned i = 0;
|
||||||
char *p = c;
|
char *p = c;
|
||||||
|
|
||||||
// CHECK: @llvm.objectsize
|
// CHECK: @llvm.objectsize
|
||||||
ObjectSize0(p);
|
ObjectSize0(p);
|
||||||
|
|
||||||
// CHECK-NOT: @llvm.objectsize
|
// Allow side-effects, since they always need to happen anyway. Just make sure
|
||||||
ObjectSize0(++p);
|
// we don't perform them twice.
|
||||||
ObjectSize0(p++);
|
// CHECK: = add
|
||||||
|
// CHECK-NOT: = add
|
||||||
|
// CHECK: @llvm.objectsize
|
||||||
|
// CHECK: call i32 @ObjectSize0
|
||||||
|
ObjectSize0(p + ++i);
|
||||||
|
|
||||||
|
// CHECK: = add
|
||||||
|
// CHECK: @llvm.objectsize
|
||||||
|
// CHECK-NOT: = add
|
||||||
|
// CHECK: call i32 @ObjectSize0
|
||||||
|
ObjectSize0(p + i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There was a bug where variadic functions with pass_object_size would cause
|
// There was a bug where variadic functions with pass_object_size would cause
|
||||||
|
@ -395,3 +405,16 @@ void test16(__attribute__((address_space(1))) unsigned *I) {
|
||||||
// CHECK: call void @pass_size_unsigned_as1
|
// CHECK: call void @pass_size_unsigned_as1
|
||||||
pass_size_unsigned_as1(I);
|
pass_size_unsigned_as1(I);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This used to cause assertion failures, since we'd try to emit the statement
|
||||||
|
// expression (and definitions for `a`) twice.
|
||||||
|
// CHECK-LABEL: define void @test17
|
||||||
|
void test17(char *C) {
|
||||||
|
// Check for 65535 to see if we're emitting this pointer twice.
|
||||||
|
// CHECK: 65535
|
||||||
|
// CHECK-NOT: 65535
|
||||||
|
// CHECK: @llvm.objectsize.i64.p0i8(i8* [[PTR:%[^,]+]],
|
||||||
|
// CHECK-NOT: 65535
|
||||||
|
// CHECK: call i32 @ObjectSize0(i8* [[PTR]]
|
||||||
|
ObjectSize0(C + ({ int a = 65535; a; }));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue