From: John McCall Date: Mon, 16 May 2011 01:05:12 +0000 (+0000) Subject: Don't actually emit calls to the reserved global placement new and delete X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b1c98a35fbd49d6404a72db4aca2ceda352380c7;p=clang Don't actually emit calls to the reserved global placement new and delete operators; their semantics are guaranteed by the language. If someone wants to argue that freestanding compiles shouldn't recognize this, I might be convinceable. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131395 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 734449005f..1d668477c1 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -444,33 +444,14 @@ CodeGenFunction::EmitSynthesizedCXXCopyCtor(llvm::Value *Dest, E->arg_begin(), E->arg_end()); } -/// Check whether the given operator new[] is the global placement -/// operator new[]. -static bool IsPlacementOperatorNewArray(ASTContext &Ctx, - const FunctionDecl *Fn) { - // Must be in global scope. Note that allocation functions can't be - // declared in namespaces. - if (!Fn->getDeclContext()->getRedeclContext()->isFileContext()) - return false; - - // Signature must be void *operator new[](size_t, void*). - // The size_t is common to all operator new[]s. - if (Fn->getNumParams() != 2) - return false; - - CanQualType ParamType = Ctx.getCanonicalType(Fn->getParamDecl(1)->getType()); - return (ParamType == Ctx.VoidPtrTy); -} - static CharUnits CalculateCookiePadding(CodeGenFunction &CGF, const CXXNewExpr *E) { if (!E->isArray()) return CharUnits::Zero(); - // No cookie is required if the new operator being used is - // ::operator new[](size_t, void*). - const FunctionDecl *OperatorNew = E->getOperatorNew(); - if (IsPlacementOperatorNewArray(CGF.getContext(), OperatorNew)) + // No cookie is required if the operator new[] being used is the + // reserved placement operator new[]. + if (E->getOperatorNew()->isReservedGlobalPlacementOperator()) return CharUnits::Zero(); return CGF.CGM.getCXXABI().GetArrayCookieSize(E); @@ -1073,11 +1054,19 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { EmitCallArg(allocatorArgs, *placementArg, placementArg->getType()); } - // Emit the allocation call. - RValue RV = - EmitCall(CGM.getTypes().getFunctionInfo(allocatorArgs, allocatorType), - CGM.GetAddrOfFunction(allocator), ReturnValueSlot(), - allocatorArgs, allocator); + // Emit the allocation call. If the allocator is a global placement + // operator, just "inline" it directly. + RValue RV; + if (allocator->isReservedGlobalPlacementOperator()) { + assert(allocatorArgs.size() == 2); + RV = allocatorArgs[1].RV; + // TODO: kill any unnecessary computations done for the size + // argument. + } else { + RV = EmitCall(CGM.getTypes().getFunctionInfo(allocatorArgs, allocatorType), + CGM.GetAddrOfFunction(allocator), ReturnValueSlot(), + allocatorArgs, allocator); + } // Emit a null check on the allocation result if the allocation // function is allowed to return null (because it has a non-throwing @@ -1122,7 +1111,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // If there's an operator delete, enter a cleanup to call it if an // exception is thrown. EHScopeStack::stable_iterator operatorDeleteCleanup; - if (E->getOperatorDelete()) { + if (E->getOperatorDelete() && + !E->getOperatorDelete()->isReservedGlobalPlacementOperator()) { EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocatorArgs); operatorDeleteCleanup = EHStack.stable_begin(); } diff --git a/test/CodeGenCXX/new.cpp b/test/CodeGenCXX/new.cpp index 10a6f7f489..d14a5e251c 100644 --- a/test/CodeGenCXX/new.cpp +++ b/test/CodeGenCXX/new.cpp @@ -1,12 +1,16 @@ // RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s -#include + +typedef __typeof__(sizeof(0)) size_t; void t1() { int* a = new int; } -// Placement. -void* operator new(size_t, void*) throw(); +// 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(); void t2(int* a) { int* b = new (a) int; @@ -163,3 +167,45 @@ void f() { delete new bool; // CHECK: ret void } + +namespace test15 { + struct A { A(); ~A(); }; + + // CHECK: define void @_ZN6test155test0EPv( + // CHECK: [[P:%.*]] = load i8* + // CHECK-NEXT: icmp eq i8* [[P]], null + // CHECK-NEXT: br i1 + // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T0]]) + void test0(void *p) { + new (p) A(); + } + + // CHECK: define void @_ZN6test155test1EPv( + // CHECK: [[P:%.*]] = load i8* + // CHECK-NEXT: icmp eq i8* [[P]], null + // CHECK-NEXT: br i1 + // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]]* [[T0]], + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T1]]) + void test1(void *p) { + new (p) A[5]; + } + + // TODO: it's okay if all these size calculations get dropped. + // FIXME: maybe we should try to throw on overflow? + // CHECK: define void @_ZN6test155test2EPvi( + // CHECK: [[N:%.*]] = load i32* + // CHECK-NEXT: [[T0:%.*]] = sext i32 [[N]] to i64 + // CHECK-NEXT: [[T1:%.*]] = icmp slt i64 [[T0]], 0 + // CHECK-NEXT: [[T2:%.*]] = select i1 [[T1]], i64 -1, i64 [[T0]] + // CHECK-NEXT: [[P:%.*]] = load i8* + // CHECK-NEXT: icmp eq i8* [[P]], null + // CHECK-NEXT: br i1 + // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]]* [[T0]], + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T1]]) + void test2(void *p, int n) { + new (p) A[n]; + } +}