diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-03-21 19:19:48 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-03-21 19:19:48 +0000 |
commit | fa752f23cc20d38259a84a1c44e508df4503f284 (patch) | |
tree | 3e70a071037e71411ba137b80b5c14dca0850e81 /clang/lib | |
parent | b17fff79f0e933b4b6955d4b308f2fa66f3d169f (diff) | |
download | bcm5719-llvm-fa752f23cc20d38259a84a1c44e508df4503f284.tar.gz bcm5719-llvm-fa752f23cc20d38259a84a1c44e508df4503f284.zip |
[Builtins] Overload __builtin_operator_new/delete to allow forwarding to usual allocation/deallocation functions.
Summary:
Libc++'s default allocator uses `__builtin_operator_new` and `__builtin_operator_delete` in order to allow the calls to new/delete to be ellided. However, libc++ now needs to support over-aligned types in the default allocator. In order to support this without disabling the existing optimization Clang needs to support calling the aligned new overloads from the builtins.
See llvm.org/PR22634 for more information about the libc++ bug.
This patch changes `__builtin_operator_new`/`__builtin_operator_delete` to call any usual `operator new`/`operator delete` function. It does this by performing overload resolution with the arguments passed to the builtin to determine which allocation function to call. If the selected function is not a usual allocation function a diagnostic is issued.
One open issue is if the `align_val_t` overloads should be considered "usual" when `LangOpts::AlignedAllocation` is disabled.
In order to allow libc++ to detect this new behavior the value for `__has_builtin(__builtin_operator_new)` has been updated to `201802`.
Reviewers: rsmith, majnemer, aaron.ballman, erik.pilkington, bogner, ahatanak
Reviewed By: rsmith
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D43047
llvm-svn: 328134
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/CodeGen/CGBuiltin.cpp | 9 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprCXX.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 2 | ||||
-rw-r--r-- | clang/lib/Lex/PPMacroExpansion.cpp | 15 | ||||
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 22 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 125 |
6 files changed, 153 insertions, 28 deletions
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 084bf50e1bc..996e5e7cd80 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2611,11 +2611,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__builtin_addressof: return RValue::get(EmitLValue(E->getArg(0)).getPointer()); case Builtin::BI__builtin_operator_new: - return EmitBuiltinNewDeleteCall(FD->getType()->castAs<FunctionProtoType>(), - E->getArg(0), false); + return EmitBuiltinNewDeleteCall( + E->getCallee()->getType()->castAs<FunctionProtoType>(), E, false); case Builtin::BI__builtin_operator_delete: - return EmitBuiltinNewDeleteCall(FD->getType()->castAs<FunctionProtoType>(), - E->getArg(0), true); + return EmitBuiltinNewDeleteCall( + E->getCallee()->getType()->castAs<FunctionProtoType>(), E, true); + case Builtin::BI__noop: // __noop always evaluates to an integer literal zero. return RValue::get(ConstantInt::get(IntTy, 0)); diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index b874959de90..56c5e681177 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1307,19 +1307,19 @@ static RValue EmitNewDeleteCall(CodeGenFunction &CGF, } RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, - const Expr *Arg, + const CallExpr *TheCall, bool IsDelete) { CallArgList Args; - const Stmt *ArgS = Arg; - EmitCallArgs(Args, *Type->param_type_begin(), llvm::makeArrayRef(ArgS)); + EmitCallArgs(Args, Type->getParamTypes(), TheCall->arguments()); // Find the allocation or deallocation function that we're calling. ASTContext &Ctx = getContext(); DeclarationName Name = Ctx.DeclarationNames .getCXXOperatorName(IsDelete ? OO_Delete : OO_New); + for (auto *Decl : Ctx.getTranslationUnitDecl()->lookup(Name)) if (auto *FD = dyn_cast<FunctionDecl>(Decl)) if (Ctx.hasSameType(FD->getType(), QualType(Type, 0))) - return EmitNewDeleteCall(*this, cast<FunctionDecl>(Decl), Type, Args); + return EmitNewDeleteCall(*this, FD, Type, Args); llvm_unreachable("predeclared global operator new/delete is missing"); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 709e7f4ad8d..7cdec6e72b6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2356,7 +2356,7 @@ public: CharUnits CookieSize = CharUnits()); RValue EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, - const Expr *Arg, bool IsDelete); + const CallExpr *TheCallExpr, bool IsDelete); llvm::Value *EmitCXXTypeidExpr(const CXXTypeidExpr *E); llvm::Value *EmitDynamicCast(Address V, const CXXDynamicCastExpr *DCE); diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 7f524c5591c..6ab737d15c8 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1801,12 +1801,21 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { [this](Token &Tok, bool &HasLexedNextToken) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); + const LangOptions &LangOpts = getLangOpts(); if (!II) return false; - else if (II->getBuiltinID() != 0) + else if (II->getBuiltinID() != 0) { + switch (II->getBuiltinID()) { + case Builtin::BI__builtin_operator_new: + case Builtin::BI__builtin_operator_delete: + // denotes date of behavior change to support calling arbitrary + // usual allocation and deallocation functions. Required by libc++ + return 201802; + default: + return true; + } return true; - else { - const LangOptions &LangOpts = getLangOpts(); + } else { return llvm::StringSwitch<bool>(II->getName()) .Case("__make_integer_seq", LangOpts.CPlusPlus) .Case("__type_pack_element", LangOpts.CPlusPlus) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index a575defe705..9c0a16c50a4 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1097,20 +1097,14 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return ExprError(); break; case Builtin::BI__builtin_operator_new: - case Builtin::BI__builtin_operator_delete: - if (!getLangOpts().CPlusPlus) { - Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language) - << (BuiltinID == Builtin::BI__builtin_operator_new - ? "__builtin_operator_new" - : "__builtin_operator_delete") - << "C++"; - return ExprError(); - } - // CodeGen assumes it can find the global new and delete to call, - // so ensure that they are declared. - DeclareGlobalNewDelete(); - break; - + case Builtin::BI__builtin_operator_delete: { + bool IsDelete = BuiltinID == Builtin::BI__builtin_operator_delete; + ExprResult Res = + SemaBuiltinOperatorNewDeleteOverloaded(TheCallResult, IsDelete); + if (Res.isInvalid()) + CorrectDelayedTyposInExpr(TheCallResult.get()); + return Res; + } // check secure string manipulation functions where overflows // are detectable at compile time case Builtin::BI__builtin___memcpy_chk: diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index a3cb462b46a..5870c8bbcba 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1443,7 +1443,7 @@ namespace { CUDAPref = S.IdentifyCUDAPreference(Caller, FD); } - operator bool() const { return FD; } + explicit operator bool() const { return FD; } bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize, bool WantAlign) const { @@ -2271,7 +2271,6 @@ static bool resolveAllocationOverload( llvm_unreachable("Unreachable, bad result from BestViableFunction"); } - /// FindAllocationFunctions - Finds the overloads of operator new and delete /// that are appropriate for the allocation. bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, @@ -3343,6 +3342,128 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, return Result; } +static bool resolveBuiltinNewDeleteOverload(Sema &S, CallExpr *TheCall, + bool IsDelete, + FunctionDecl *&Operator) { + + DeclarationName NewName = S.Context.DeclarationNames.getCXXOperatorName( + IsDelete ? OO_Delete : OO_New); + + LookupResult R(S, NewName, TheCall->getLocStart(), Sema::LookupOrdinaryName); + S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl()); + assert(!R.empty() && "implicitly declared allocation functions not found"); + assert(!R.isAmbiguous() && "global allocation functions are ambiguous"); + + // We do our own custom access checks below. + R.suppressDiagnostics(); + + SmallVector<Expr *, 8> Args(TheCall->arg_begin(), TheCall->arg_end()); + OverloadCandidateSet Candidates(R.getNameLoc(), + OverloadCandidateSet::CSK_Normal); + for (LookupResult::iterator FnOvl = R.begin(), FnOvlEnd = R.end(); + FnOvl != FnOvlEnd; ++FnOvl) { + // Even member operator new/delete are implicitly treated as + // static, so don't use AddMemberCandidate. + NamedDecl *D = (*FnOvl)->getUnderlyingDecl(); + + if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { + S.AddTemplateOverloadCandidate(FnTemplate, FnOvl.getPair(), + /*ExplicitTemplateArgs=*/nullptr, Args, + Candidates, + /*SuppressUserConversions=*/false); + continue; + } + + FunctionDecl *Fn = cast<FunctionDecl>(D); + S.AddOverloadCandidate(Fn, FnOvl.getPair(), Args, Candidates, + /*SuppressUserConversions=*/false); + } + + SourceRange Range = TheCall->getSourceRange(); + + // Do the resolution. + OverloadCandidateSet::iterator Best; + switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) { + case OR_Success: { + // Got one! + FunctionDecl *FnDecl = Best->Function; + assert(R.getNamingClass() == nullptr && + "class members should not be considered"); + + if (!FnDecl->isReplaceableGlobalAllocationFunction()) { + S.Diag(R.getNameLoc(), diag::err_builtin_operator_new_delete_not_usual) + << (IsDelete ? 1 : 0) << Range; + S.Diag(FnDecl->getLocation(), diag::note_non_usual_function_declared_here) + << R.getLookupName() << FnDecl->getSourceRange(); + return true; + } + + Operator = FnDecl; + return false; + } + + case OR_No_Viable_Function: + S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call) + << R.getLookupName() << Range; + Candidates.NoteCandidates(S, OCD_AllCandidates, Args); + return true; + + case OR_Ambiguous: + S.Diag(R.getNameLoc(), diag::err_ovl_ambiguous_call) + << R.getLookupName() << Range; + Candidates.NoteCandidates(S, OCD_ViableCandidates, Args); + return true; + + case OR_Deleted: { + S.Diag(R.getNameLoc(), diag::err_ovl_deleted_call) + << Best->Function->isDeleted() << R.getLookupName() + << S.getDeletedOrUnavailableSuffix(Best->Function) << Range; + Candidates.NoteCandidates(S, OCD_AllCandidates, Args); + return true; + } + } + llvm_unreachable("Unreachable, bad result from BestViableFunction"); +} + +ExprResult +Sema::SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult, + bool IsDelete) { + CallExpr *TheCall = cast<CallExpr>(TheCallResult.get()); + if (!getLangOpts().CPlusPlus) { + Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language) + << (IsDelete ? "__builtin_operator_delete" : "__builtin_operator_new") + << "C++"; + return ExprError(); + } + // CodeGen assumes it can find the global new and delete to call, + // so ensure that they are declared. + DeclareGlobalNewDelete(); + + FunctionDecl *OperatorNewOrDelete = nullptr; + if (resolveBuiltinNewDeleteOverload(*this, TheCall, IsDelete, + OperatorNewOrDelete)) + return ExprError(); + assert(OperatorNewOrDelete && "should be found"); + + TheCall->setType(OperatorNewOrDelete->getReturnType()); + for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) { + QualType ParamTy = OperatorNewOrDelete->getParamDecl(i)->getType(); + InitializedEntity Entity = + InitializedEntity::InitializeParameter(Context, ParamTy, false); + ExprResult Arg = PerformCopyInitialization( + Entity, TheCall->getArg(i)->getLocStart(), TheCall->getArg(i)); + if (Arg.isInvalid()) + return ExprError(); + TheCall->setArg(i, Arg.get()); + } + auto Callee = dyn_cast<ImplicitCastExpr>(TheCall->getCallee()); + assert(Callee && Callee->getCastKind() == CK_BuiltinFnToFnPtr && + "Callee expected to be implicit cast to a builtin function pointer"); + Callee->setType(OperatorNewOrDelete->getType()); + + return TheCallResult; +} + void Sema::CheckVirtualDtorCall(CXXDestructorDecl *dtor, SourceLocation Loc, bool IsDelete, bool CallCanBeVirtual, bool WarnOnNonAbstractTypes, |