Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -1371,8 +1371,9 @@ // Clang builtins (not available in GCC). BUILTIN(__builtin_addressof, "v*v&", "nct") -BUILTIN(__builtin_operator_new, "v*z", "c") -BUILTIN(__builtin_operator_delete, "vv*", "n") +BUILTIN(__builtin_operator_new, "v*z", "tc") +BUILTIN(__builtin_operator_delete, "vv*", "tn") + BUILTIN(__builtin_char_memchr, "c*cC*iz", "n") // Safestack builtins Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6510,10 +6510,15 @@ def warn_aligned_allocation_unavailable :Warning< "aligned %select{allocation|deallocation}0 function of type '%1' is only " "available on %2 %3 or newer">, InGroup, DefaultError; +def warn_aligned_builtin_operator_new_unavailable :Warning< + "aligned %select{__builtin_operator_new|__builtin_operator_delete}0 function is only " + "available on %2 %3 or newer">, InGroup, DefaultError; def note_silence_unligned_allocation_unavailable : Note< "if you supply your own aligned allocation functions, use " "-Wno-aligned-allocation-unavailable to silence this diagnostic">; - +def err_builtin_operator_new_requires_aligned_allocation : Error< + "%select{__builtin_operator_new|__builtin_operator_delete}0 with alignment " + "requires aligned allocation support; use -faligned-allocation to enable">; def err_conditional_void_nonvoid : Error< "%select{left|right}1 operand to ? is void, but %select{right|left}1 operand " "is of type %0">; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -10363,6 +10363,8 @@ ExprResult SemaBuiltinNontemporalOverloaded(ExprResult TheCallResult); ExprResult SemaAtomicOpsOverloaded(ExprResult TheCallResult, AtomicExpr::AtomicOp Op); + ExprResult SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult, + bool IsDelete); bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum, llvm::APSInt &Result); bool SemaBuiltinConstantArgRange(CallExpr *TheCall, int ArgNum, Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -2612,10 +2612,11 @@ return RValue::get(EmitLValue(E->getArg(0)).getPointer()); case Builtin::BI__builtin_operator_new: return EmitBuiltinNewDeleteCall(FD->getType()->castAs(), - E->getArg(0), false); + E, false); case Builtin::BI__builtin_operator_delete: return EmitBuiltinNewDeleteCall(FD->getType()->castAs(), - E->getArg(0), true); + E, true); + case Builtin::BI__noop: // __noop always evaluates to an integer literal zero. return RValue::get(ConstantInt::get(IntTy, 0)); Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -1307,19 +1307,39 @@ } RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, - const Expr *Arg, - bool IsDelete) { + const Expr *E, bool IsDelete) { + const auto *TheCall = dyn_cast(E); + llvm::SmallVector ArgTypes; + for (auto *Arg : TheCall->arguments()) + ArgTypes.push_back(Arg->getType()); CallArgList Args; - const Stmt *ArgS = Arg; - EmitCallArgs(Args, *Type->param_type_begin(), llvm::makeArrayRef(ArgS)); + EmitCallArgs(Args, ArgTypes, 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); + + auto IsMatchingDecl = [&](FunctionDecl *FD) { + if (TheCall->getNumArgs() != FD->getNumParams()) + return false; + if (!Ctx.hasSameType(TheCall->getArg(0)->getType(), + FD->getParamDecl(0)->getType())) + return false; + if (TheCall->getNumArgs() == 2 && + !FD->getParamDecl(1)->getType()->isAlignValT()) + return false; + // FIXME: Should we be comparing things like the exception type of the + // functions to ensure it perfectly matches the standard? + if (Type->getExtInfo() != + FD->getType()->getAs()->getExtInfo()) + return false; + return true; + }; + for (auto *Decl : Ctx.getTranslationUnitDecl()->lookup(Name)) if (auto *FD = dyn_cast(Decl)) - if (Ctx.hasSameType(FD->getType(), QualType(Type, 0))) - return EmitNewDeleteCall(*this, cast(Decl), Type, Args); + if (IsMatchingDecl(FD)) + return EmitNewDeleteCall(*this, FD, Type, Args); llvm_unreachable("predeclared global operator new/delete is missing"); } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -2346,7 +2346,7 @@ CharUnits CookieSize = CharUnits()); RValue EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, - const Expr *Arg, bool IsDelete); + const Expr *TheCallExpr, bool IsDelete); llvm::Value *EmitCXXTypeidExpr(const CXXTypeidExpr *E); llvm::Value *EmitDynamicCast(Address V, const CXXDynamicCastExpr *DCE); Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1238,6 +1238,10 @@ // NOTE: For features covered by SD-6, it is preferable to provide *only* // the SD-6 macro and not a __has_feature check. + // Clang specific changes which libc++ needs to be able to detect + .Case("has_aligned_builtin_operator_new", + LangOpts.CPlusPlus && LangOpts.AlignedAllocation) + // C++ TSes //.Case("cxx_runtime_arrays", LangOpts.CPlusPlusTSArrays) //.Case("cxx_concepts", LangOpts.CPlusPlusTSConcepts) Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -36,6 +36,7 @@ #include "clang/AST/UnresolvedSet.h" #include "clang/Analysis/Analyses/FormatString.h" #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/AlignedAllocation.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/IdentifierTable.h" @@ -1097,20 +1098,14 @@ 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: @@ -2904,6 +2899,71 @@ } } +ExprResult +Sema::SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult, + bool IsDelete) { + CallExpr *TheCall = cast(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(); + + TheCall->setType(IsDelete ? Context.VoidTy : Context.VoidPtrTy); + + if (TheCall->getNumArgs() < 1) { + Diag(TheCall->getLocEnd(), diag::err_typecheck_call_too_few_args) + << 0 << 1 << TheCall->getNumArgs() + << TheCall->getCallee()->getSourceRange(); + return ExprError(); + } else if (TheCall->getNumArgs() > 2) { + Diag(TheCall->getArg(2)->getLocStart(), + diag::err_typecheck_call_too_many_args) + << 0 << 2 << TheCall->getNumArgs() + << TheCall->getCallee()->getSourceRange(); + return ExprError(); + } + + llvm::SmallVector ArgTypes; + ArgTypes.push_back(IsDelete ? Context.VoidPtrTy : Context.getSizeType()); + + if (TheCall->getNumArgs() == 2) { + if (!getLangOpts().AlignedAllocation) { + Diag(TheCall->getArg(1)->getLocStart(), + diag::err_builtin_operator_new_requires_aligned_allocation) + << IsDelete << TheCall->getSourceRange(); + return ExprError(); + } + if (getLangOpts().AlignedAllocationUnavailable) { + const llvm::Triple &T = Context.getTargetInfo().getTriple(); + StringRef OSName = AvailabilityAttr::getPlatformNameSourceSpelling( + Context.getTargetInfo().getPlatformName()); + auto Loc = TheCall->getExprLoc(); + Diag(Loc, diag::warn_aligned_builtin_operator_new_unavailable) + << IsDelete << OSName + << alignedAllocMinVersion(T.getOS()).getAsString(); + Diag(Loc, diag::note_silence_unligned_allocation_unavailable); + } + ArgTypes.push_back(getStdAlignValT()->getIntegerType()); + } + + for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) { + InitializedEntity Entity = + InitializedEntity::InitializeParameter(Context, ArgTypes[i], false); + ExprResult Arg = TheCall->getArg(i); + Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (Arg.isInvalid()) + return ExprError(); + TheCall->setArg(i, Arg.get()); + } + + return TheCallResult; +} + ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, AtomicExpr::AtomicOp Op) { CallExpr *TheCall = cast(TheCallResult.get()); Index: test/CodeGenCXX/new.cpp =================================================================== --- test/CodeGenCXX/new.cpp +++ test/CodeGenCXX/new.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -verify -faligned-allocation \ +// RUN: -emit-llvm -o - | FileCheck %s typedef __typeof__(sizeof(0)) size_t; @@ -363,6 +364,12 @@ // CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE]] __builtin_operator_delete(__builtin_operator_new(4)); } + // CHECK-LABEL: define void @_ZN8builtins1gEv + void g() { + // CHECK: call i8* @_ZnwmSt11align_val_t(i64 4, i64 4) [[ATTR_BUILTIN_NEW]] + // CHECK: call void @_ZdlPvSt11align_val_t({{.*}}, i64 4) [[ATTR_BUILTIN_DELETE]] + __builtin_operator_delete(__builtin_operator_new(4, 4), 4); + } } // CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = {{[{].*}} nobuiltin {{.*[}]}} Index: test/Lexer/has_feature_aligned_builtin_new.cpp =================================================================== --- /dev/null +++ test/Lexer/has_feature_aligned_builtin_new.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c++11 %s -o - | \ +// RUN: FileCheck --check-prefix=CHECK-NO-BUILTIN %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c++11 -faligned-allocation %s -o - | \ +// RUN: FileCheck --check-prefix=CHECK-HAS-BUILTIN %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c++1z %s -o - | \ +// RUN: FileCheck --check-prefix=CHECK-HAS-BUILTIN %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c++1z -fno-aligned-allocation %s -o - | \ +// RUN: FileCheck --check-prefix=CHECK-NO-BUILTIN %s + +#if __has_feature(has_aligned_builtin_operator_new) +void has_builtin(); +#else +void no_builtin(); +#endif + +// CHECK-HAS-BUILTIN: has_builtin +// CHECK-NO-BUILTIN: no_builtin Index: test/SemaCXX/builtin-operator-new-delete-alignment-disabled.cpp =================================================================== --- /dev/null +++ test/SemaCXX/builtin-operator-new-delete-alignment-disabled.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -std=c++03 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++03 -faligned-allocation -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++1z -fno-aligned-allocation -fsyntax-only -verify %s + +void test_disabled() { +#ifdef __cpp_aligned_new + // expected-no-diagnostics +#else + // expected-error@+3 {{__builtin_operator_new with alignment requires aligned allocation support; use -faligned-allocation to enable}} + // expected-error@+3 {{__builtin_operator_delete with alignment requires aligned allocation support; use -faligned-allocation to enable}} +#endif + (void)__builtin_operator_new(1, 2); + __builtin_operator_delete((void *)0, 2); +} Index: test/SemaCXX/builtin-operator-new-delete.cpp =================================================================== --- /dev/null +++ test/SemaCXX/builtin-operator-new-delete.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++1z -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++03 -faligned-allocation -fsyntax-only -verify %s + +void *NP = 0; + +void test_signature() { + __builtin_operator_new(); // expected-error {{too few arguments to function call, expected 1, have 0}} + __builtin_operator_new(0, 0, 0); // expected-error {{too many arguments to function call, expected 2, have 3}} + __builtin_operator_delete(); // expected-error {{too few arguments to function call, expected 1, have 0}} + __builtin_operator_delete(0, 0, 0); // expected-error {{too many arguments to function call, expected 2, have 3}} +} + +void test_typo_in_args() { + __builtin_operator_new(DNE); // expected-error {{undeclared identifier 'DNE'}} + __builtin_operator_new(DNE, DNE2); // expected-error {{undeclared identifier 'DNE'}} expected-error {{'DNE2'}} + __builtin_operator_delete(DNE); // expected-error {{'DNE'}} + __builtin_operator_delete(DNE, DNE2); // expected-error {{'DNE'}} expected-error {{'DNE2'}} +} + +void test_arg_types() { + __builtin_operator_new(NP); // expected-error {{cannot initialize a parameter of type 'unsigned long' with an lvalue of type 'void *'}} + __builtin_operator_new(NP, 0); // expected-error {{cannot initialize a parameter of type 'unsigned long' with an lvalue of type 'void *'}} +} + +void test_return_type() { + int w = __builtin_operator_new(42); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}} + int x = __builtin_operator_new(42, 42); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}} + int y = __builtin_operator_delete(NP); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void'}} + int z = __builtin_operator_delete(NP, 42); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void'}} +}