mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 15:41:46 +00:00
Tighten up the set of operator new/operator delete calls we're permitted to
optimize, to follow the permissions granted in N3664. Under those rules, only calls generated by new-expressions and delete-expressions are permitted to be optimized, and direct calls to ::operator new and ::operator delete must be treated as normal calls. llvm-svn: 186799
This commit is contained in:
parent
70523c7926
commit
8d0dc31dca
@ -1720,6 +1720,21 @@ public:
|
||||
/// This function must be an allocation or deallocation function.
|
||||
bool isReservedGlobalPlacementOperator() const;
|
||||
|
||||
/// \brief Determines whether this function is one of the replaceable
|
||||
/// global allocation functions:
|
||||
/// void *operator new(size_t);
|
||||
/// void *operator new(size_t, const std::nothrow_t &) noexcept;
|
||||
/// void *operator new[](size_t);
|
||||
/// void *operator new[](size_t, const std::nothrow_t &) noexcept;
|
||||
/// void operator delete(void *) noexcept;
|
||||
/// void operator delete(void *, const std::nothrow_t &) noexcept;
|
||||
/// void operator delete[](void *) noexcept;
|
||||
/// void operator delete[](void *, const std::nothrow_t &) noexcept;
|
||||
/// These functions have special behavior under C++1y [expr.new]:
|
||||
/// An implementation is allowed to omit a call to a replaceable global
|
||||
/// allocation function. [...]
|
||||
bool isReplaceableGlobalAllocationFunction() const;
|
||||
|
||||
/// Compute the language linkage.
|
||||
LanguageLinkage getLanguageLinkage() const;
|
||||
|
||||
|
@ -2132,13 +2132,18 @@ void FunctionDecl::setPure(bool P) {
|
||||
Parent->markedVirtualFunctionPure();
|
||||
}
|
||||
|
||||
template<std::size_t Len>
|
||||
static bool isNamed(const NamedDecl *ND, const char (&Str)[Len]) {
|
||||
IdentifierInfo *II = ND->getIdentifier();
|
||||
return II && II->isStr(Str);
|
||||
}
|
||||
|
||||
bool FunctionDecl::isMain() const {
|
||||
const TranslationUnitDecl *tunit =
|
||||
dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext());
|
||||
return tunit &&
|
||||
!tunit->getASTContext().getLangOpts().Freestanding &&
|
||||
getIdentifier() &&
|
||||
getIdentifier()->isStr("main");
|
||||
isNamed(this, "main");
|
||||
}
|
||||
|
||||
bool FunctionDecl::isReservedGlobalPlacementOperator() const {
|
||||
@ -2163,6 +2168,47 @@ bool FunctionDecl::isReservedGlobalPlacementOperator() const {
|
||||
return (proto->getArgType(1).getCanonicalType() == Context.VoidPtrTy);
|
||||
}
|
||||
|
||||
static bool isNamespaceStd(const DeclContext *DC) {
|
||||
const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC->getRedeclContext());
|
||||
return ND && isNamed(ND, "std") &&
|
||||
ND->getParent()->getRedeclContext()->isTranslationUnit();
|
||||
}
|
||||
|
||||
bool FunctionDecl::isReplaceableGlobalAllocationFunction() const {
|
||||
if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
|
||||
return false;
|
||||
if (getDeclName().getCXXOverloadedOperator() != OO_New &&
|
||||
getDeclName().getCXXOverloadedOperator() != OO_Delete &&
|
||||
getDeclName().getCXXOverloadedOperator() != OO_Array_New &&
|
||||
getDeclName().getCXXOverloadedOperator() != OO_Array_Delete)
|
||||
return false;
|
||||
|
||||
if (isa<CXXRecordDecl>(getDeclContext()))
|
||||
return false;
|
||||
assert(getDeclContext()->getRedeclContext()->isTranslationUnit());
|
||||
|
||||
const FunctionProtoType *FPT = getType()->castAs<FunctionProtoType>();
|
||||
if (FPT->getNumArgs() > 2 || FPT->isVariadic())
|
||||
return false;
|
||||
|
||||
// If this is a single-parameter function, it must be a replaceable global
|
||||
// allocation or deallocation function.
|
||||
if (FPT->getNumArgs() == 1)
|
||||
return true;
|
||||
|
||||
// Otherwise, we're looking for a second parameter whose type is
|
||||
// 'const std::nothrow_t &'.
|
||||
QualType Ty = FPT->getArgType(1);
|
||||
if (!Ty->isReferenceType())
|
||||
return false;
|
||||
Ty = Ty->getPointeeType();
|
||||
if (Ty.getCVRQualifiers() != Qualifiers::Const)
|
||||
return false;
|
||||
// FIXME: Recognise nothrow_t in an inline namespace inside std?
|
||||
const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
|
||||
return RD && isNamed(RD, "nothrow_t") && isNamespaceStd(RD->getDeclContext());
|
||||
}
|
||||
|
||||
LanguageLinkage FunctionDecl::getLanguageLinkage() const {
|
||||
// Users expect to be able to write
|
||||
// extern "C" void *__builtin_alloca (size_t);
|
||||
|
@ -1002,6 +1002,38 @@ static void EmitNewInitializer(CodeGenFunction &CGF, const CXXNewExpr *E,
|
||||
StoreAnyExprIntoOneUnit(CGF, Init, E->getAllocatedType(), NewPtr);
|
||||
}
|
||||
|
||||
/// Emit a call to an operator new or operator delete function, as implicitly
|
||||
/// created by new-expressions and delete-expressions.
|
||||
static RValue EmitNewDeleteCall(CodeGenFunction &CGF,
|
||||
const FunctionDecl *Callee,
|
||||
const FunctionProtoType *CalleeType,
|
||||
const CallArgList &Args) {
|
||||
llvm::Instruction *CallOrInvoke;
|
||||
RValue RV =
|
||||
CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(Args, CalleeType),
|
||||
CGF.CGM.GetAddrOfFunction(Callee), ReturnValueSlot(), Args,
|
||||
Callee, &CallOrInvoke);
|
||||
|
||||
/// C++1y [expr.new]p10:
|
||||
/// [In a new-expression,] an implementation is allowed to omit a call
|
||||
/// to a replaceable global allocation function.
|
||||
///
|
||||
/// We model such elidable calls with the 'builtin' attribute.
|
||||
if (Callee->isReplaceableGlobalAllocationFunction()) {
|
||||
// FIXME: Add addAttribute to CallSite.
|
||||
if (llvm::CallInst *CI = dyn_cast<llvm::CallInst>(CallOrInvoke))
|
||||
CI->addAttribute(llvm::AttributeSet::FunctionIndex,
|
||||
llvm::Attribute::Builtin);
|
||||
else if (llvm::InvokeInst *II = dyn_cast<llvm::InvokeInst>(CallOrInvoke))
|
||||
II->addAttribute(llvm::AttributeSet::FunctionIndex,
|
||||
llvm::Attribute::Builtin);
|
||||
else
|
||||
llvm_unreachable("unexpected kind of call instruction");
|
||||
}
|
||||
|
||||
return RV;
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// A cleanup to call the given 'operator delete' function upon
|
||||
/// abnormal exit from a new expression.
|
||||
@ -1051,9 +1083,7 @@ namespace {
|
||||
DeleteArgs.add(getPlacementArgs()[I], *AI++);
|
||||
|
||||
// Call 'operator delete'.
|
||||
CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, FPT),
|
||||
CGF.CGM.GetAddrOfFunction(OperatorDelete),
|
||||
ReturnValueSlot(), DeleteArgs, OperatorDelete);
|
||||
EmitNewDeleteCall(CGF, OperatorDelete, FPT, DeleteArgs);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1112,9 +1142,7 @@ namespace {
|
||||
}
|
||||
|
||||
// Call 'operator delete'.
|
||||
CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, FPT),
|
||||
CGF.CGM.GetAddrOfFunction(OperatorDelete),
|
||||
ReturnValueSlot(), DeleteArgs, OperatorDelete);
|
||||
EmitNewDeleteCall(CGF, OperatorDelete, FPT, DeleteArgs);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1227,10 +1255,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
||||
// TODO: kill any unnecessary computations done for the size
|
||||
// argument.
|
||||
} else {
|
||||
RV = EmitCall(CGM.getTypes().arrangeFreeFunctionCall(allocatorArgs,
|
||||
allocatorType),
|
||||
CGM.GetAddrOfFunction(allocator), ReturnValueSlot(),
|
||||
allocatorArgs, allocator);
|
||||
RV = EmitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs);
|
||||
}
|
||||
|
||||
// Emit a null check on the allocation result if the allocation
|
||||
@ -1350,9 +1375,7 @@ void CodeGenFunction::EmitDeleteCall(const FunctionDecl *DeleteFD,
|
||||
DeleteArgs.add(RValue::get(Size), SizeTy);
|
||||
|
||||
// Emit the call to delete.
|
||||
EmitCall(CGM.getTypes().arrangeFreeFunctionCall(DeleteArgs, DeleteFTy),
|
||||
CGM.GetAddrOfFunction(DeleteFD), ReturnValueSlot(),
|
||||
DeleteArgs, DeleteFD);
|
||||
EmitNewDeleteCall(*this, DeleteFD, DeleteFTy, DeleteArgs);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -1508,9 +1531,7 @@ namespace {
|
||||
}
|
||||
|
||||
// Emit the call to delete.
|
||||
CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Args, DeleteFTy),
|
||||
CGF.CGM.GetAddrOfFunction(OperatorDelete),
|
||||
ReturnValueSlot(), Args, OperatorDelete);
|
||||
EmitNewDeleteCall(CGF, OperatorDelete, DeleteFTy, Args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -744,6 +744,12 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD,
|
||||
|
||||
if (const SectionAttr *SA = FD->getAttr<SectionAttr>())
|
||||
F->setSection(SA->getName());
|
||||
|
||||
// A replaceable global allocation function does not act like a builtin by
|
||||
// default, only if it is invoked by a new-expression or delete-expression.
|
||||
if (FD->isReplaceableGlobalAllocationFunction())
|
||||
F->addAttribute(llvm::AttributeSet::FunctionIndex,
|
||||
llvm::Attribute::NoBuiltin);
|
||||
}
|
||||
|
||||
void CodeGenModule::AddUsedGlobal(llvm::GlobalValue *GV) {
|
||||
|
@ -2,16 +2,38 @@
|
||||
|
||||
typedef __typeof__(sizeof(0)) size_t;
|
||||
|
||||
// Ensure that this declaration doesn't cause operator new to lose its
|
||||
// 'noalias' attribute.
|
||||
void *operator new[](size_t);
|
||||
|
||||
void t1() {
|
||||
int* a = new int;
|
||||
delete new int;
|
||||
delete [] new int [3];
|
||||
}
|
||||
|
||||
// CHECK: declare noalias i8* @_Znwm(i64) [[ATTR_NOBUILTIN:#[^ ]*]]
|
||||
// CHECK: declare void @_ZdlPv(i8*) [[ATTR_NOBUILTIN_NOUNWIND:#[^ ]*]]
|
||||
// CHECK: declare noalias i8* @_Znam(i64) [[ATTR_NOBUILTIN]]
|
||||
// CHECK: declare void @_ZdaPv(i8*) [[ATTR_NOBUILTIN_NOUNWIND]]
|
||||
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
}
|
||||
std::nothrow_t nothrow;
|
||||
|
||||
// Declare the reserved placement operators.
|
||||
void *operator new(size_t, void*) throw();
|
||||
void operator delete(void*, void*) throw();
|
||||
void *operator new[](size_t, void*) throw();
|
||||
void operator delete[](void*, void*) throw();
|
||||
|
||||
// Declare the replaceable global allocation operators.
|
||||
void *operator new(size_t, const std::nothrow_t &) throw();
|
||||
void *operator new[](size_t, const std::nothrow_t &) throw();
|
||||
void operator delete(void *, const std::nothrow_t &) throw();
|
||||
void operator delete[](void *, const std::nothrow_t &) throw();
|
||||
|
||||
|
||||
void t2(int* a) {
|
||||
int* b = new (a) int;
|
||||
}
|
||||
@ -77,10 +99,6 @@ void t8(int n) {
|
||||
new U[n];
|
||||
}
|
||||
|
||||
// noalias
|
||||
// CHECK: declare noalias i8* @_Znam
|
||||
void *operator new[](size_t);
|
||||
|
||||
void t9() {
|
||||
bool b;
|
||||
|
||||
@ -260,3 +278,63 @@ namespace PR13380 {
|
||||
// CHECK-NEXT: call void @_ZN7PR133801BC1Ev
|
||||
void* f() { return new B[2](); }
|
||||
}
|
||||
|
||||
struct MyPlacementType {} mpt;
|
||||
void *operator new(size_t, MyPlacementType);
|
||||
|
||||
namespace N3664 {
|
||||
struct S { S() throw(int); };
|
||||
|
||||
// CHECK-LABEL: define void @_ZN5N36641fEv
|
||||
void f() {
|
||||
// CHECK: call noalias i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]
|
||||
int *p = new int;
|
||||
// CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]]
|
||||
delete p;
|
||||
|
||||
// CHECK: call noalias i8* @_Znam(i64 12) [[ATTR_BUILTIN_NEW]]
|
||||
int *q = new int[3];
|
||||
// CHECK: call void @_ZdaPv({{.*}}) [[ATTR_BUILTIN_DELETE]]
|
||||
delete [] p;
|
||||
|
||||
// CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) [[ATTR_BUILTIN_NOTHROW_NEW:#[^ ]*]]
|
||||
(void) new (nothrow) S[3];
|
||||
|
||||
// CHECK: call i8* @_Znwm15MyPlacementType(i64 4){{$}}
|
||||
(void) new (mpt) int;
|
||||
}
|
||||
|
||||
// FIXME: Can we mark this noalias?
|
||||
// CHECK: declare i8* @_ZnamRKSt9nothrow_t(i64, {{.*}}) [[ATTR_NOBUILTIN_NOUNWIND]]
|
||||
|
||||
// CHECK-LABEL: define void @_ZN5N36641gEv
|
||||
void g() {
|
||||
// It's OK for there to be attributes here, so long as we don't have a
|
||||
// 'builtin' attribute.
|
||||
// CHECK: call noalias i8* @_Znwm(i64 4){{$}}
|
||||
int *p = (int*)operator new(4);
|
||||
// CHECK: call void @_ZdlPv({{.*}}) [[ATTR_NOUNWIND:#[^ ]*]]
|
||||
operator delete(p);
|
||||
|
||||
// CHECK: call noalias i8* @_Znam(i64 12){{$}}
|
||||
int *q = (int*)operator new[](12);
|
||||
// CHECK: call void @_ZdaPv({{.*}}) [[ATTR_NOUNWIND]]
|
||||
operator delete [](p);
|
||||
|
||||
// CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) [[ATTR_NOUNWIND]]
|
||||
(void) operator new[](3, nothrow);
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = {{[{].*}} nobuiltin {{.*[}]}}
|
||||
// CHECK-DAG: attributes [[ATTR_NOBUILTIN_NOUNWIND]] = {{[{].*}} nobuiltin nounwind {{.*[}]}}
|
||||
|
||||
// CHECK: attributes [[ATTR_NOUNWIND]] =
|
||||
// CHECK-NOT: builtin
|
||||
// CHECK-NOT: attributes
|
||||
// CHECK: nounwind
|
||||
// CHECK-NOT: builtin
|
||||
// CHECK: attributes
|
||||
|
||||
// CHECK-DAG: attributes [[ATTR_BUILTIN_NEW]] = {{[{].*}} builtin {{.*[}]}}
|
||||
// CHECK-DAG: attributes [[ATTR_BUILTIN_DELETE]] = {{[{].*}} builtin {{.*[}]}}
|
||||
|
@ -467,7 +467,7 @@ available.</p>
|
||||
<tr>
|
||||
<td>Clarifying memory allocation</td>
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html">N3664</a></td>
|
||||
<td class="partial" align="center">Partial</td>
|
||||
<td class="svn" align="center">SVN</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user