From 78cf9b830c78daa5aed51b5ca77bb7db9974d1ec Mon Sep 17 00:00:00 2001 From: Timm Baeder Date: Sat, 7 Sep 2024 12:17:54 +0200 Subject: [PATCH] [clang][bytecode] Implement using operator new/operator delete (#107679) Reuse the __builtin_operator_{new,delete} implementations. --- clang/lib/AST/ByteCode/Compiler.cpp | 31 ++++++++++++++++-------- clang/lib/AST/ByteCode/Compiler.h | 2 +- clang/lib/AST/ByteCode/Interp.cpp | 4 +-- clang/lib/AST/ByteCode/Interp.h | 4 +-- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 4 +-- clang/lib/AST/ByteCode/Opcodes.td | 2 +- clang/test/AST/ByteCode/new-delete.cpp | 14 +++++++++++ 7 files changed, 43 insertions(+), 18 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index bada8621b9681f..115b0aa7dd29c9 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -4070,18 +4070,18 @@ bool Compiler::visitAPValueInitializer(const APValue &Val, } template -bool Compiler::VisitBuiltinCallExpr(const CallExpr *E) { +bool Compiler::VisitBuiltinCallExpr(const CallExpr *E, + unsigned BuiltinID) { const Function *Func = getFunction(E->getDirectCallee()); if (!Func) return false; // For these, we're expected to ultimately return an APValue pointing // to the CallExpr. This is needed to get the correct codegen. - unsigned Builtin = E->getBuiltinCallee(); - if (Builtin == Builtin::BI__builtin___CFStringMakeConstantString || - Builtin == Builtin::BI__builtin___NSStringMakeConstantString || - Builtin == Builtin::BI__builtin_ptrauth_sign_constant || - Builtin == Builtin::BI__builtin_function_start) { + if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString || + BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString || + BuiltinID == Builtin::BI__builtin_ptrauth_sign_constant || + BuiltinID == Builtin::BI__builtin_function_start) { if (std::optional GlobalOffset = P.createGlobal(E)) { if (!this->emitGetPtrGlobal(*GlobalOffset, E)) return false; @@ -4113,7 +4113,7 @@ bool Compiler::VisitBuiltinCallExpr(const CallExpr *E) { } } - if (!this->emitCallBI(Func, E, E)) + if (!this->emitCallBI(Func, E, BuiltinID, E)) return false; if (DiscardResult && !ReturnType->isVoidType()) { @@ -4126,13 +4126,24 @@ bool Compiler::VisitBuiltinCallExpr(const CallExpr *E) { template bool Compiler::VisitCallExpr(const CallExpr *E) { - if (E->getBuiltinCallee()) - return VisitBuiltinCallExpr(E); + if (unsigned BuiltinID = E->getBuiltinCallee()) + return VisitBuiltinCallExpr(E, BuiltinID); + + const FunctionDecl *FuncDecl = E->getDirectCallee(); + // Calls to replaceable operator new/operator delete. + if (FuncDecl && FuncDecl->isReplaceableGlobalAllocationFunction()) { + if (FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_New || + FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_Array_New) { + return VisitBuiltinCallExpr(E, Builtin::BI__builtin_operator_new); + } else { + assert(FuncDecl->getDeclName().getCXXOverloadedOperator() == OO_Delete); + return VisitBuiltinCallExpr(E, Builtin::BI__builtin_operator_delete); + } + } QualType ReturnType = E->getCallReturnType(Ctx.getASTContext()); std::optional T = classify(ReturnType); bool HasRVO = !ReturnType->isVoidType() && !T; - const FunctionDecl *FuncDecl = E->getDirectCallee(); if (HasRVO) { if (DiscardResult) { diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index eb1252fec054bb..39c0736cb4e27e 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -133,7 +133,7 @@ class Compiler : public ConstStmtVisitor, bool>, bool VisitVectorBinOp(const BinaryOperator *E); bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); bool VisitCallExpr(const CallExpr *E); - bool VisitBuiltinCallExpr(const CallExpr *E); + bool VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinID); bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 99d01ca52645e0..ac02bd6d033487 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1178,7 +1178,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func, } bool CallBI(InterpState &S, CodePtr &PC, const Function *Func, - const CallExpr *CE) { + const CallExpr *CE, uint32_t BuiltinID) { if (S.checkingPotentialConstantExpression()) return false; auto NewFrame = std::make_unique(S, Func, PC); @@ -1186,7 +1186,7 @@ bool CallBI(InterpState &S, CodePtr &PC, const Function *Func, InterpFrame *FrameBefore = S.Current; S.Current = NewFrame.get(); - if (InterpretBuiltin(S, PC, Func, CE)) { + if (InterpretBuiltin(S, PC, Func, CE, BuiltinID)) { NewFrame.release(); return true; } diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 4ca0e05d67c7c3..7a0011b9606976 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -155,7 +155,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func, bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize); bool CallBI(InterpState &S, CodePtr &PC, const Function *Func, - const CallExpr *CE); + const CallExpr *CE, uint32_t BuiltinID); bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, const CallExpr *CE); @@ -268,7 +268,7 @@ bool Interpret(InterpState &S, APValue &Result); /// Interpret a builtin function. bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, - const CallExpr *Call); + const CallExpr *Call, uint32_t BuiltinID); /// Interpret an offsetof operation. bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 49fbaa3cbcb316..51c77b7da1a655 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1395,13 +1395,13 @@ static bool interp__builtin_operator_delete(InterpState &S, CodePtr OpPC, } bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, - const CallExpr *Call) { + const CallExpr *Call, uint32_t BuiltinID) { const InterpFrame *Frame = S.Current; APValue Dummy; std::optional ReturnT = S.getContext().classify(Call); - switch (F->getBuiltinID()) { + switch (BuiltinID) { case Builtin::BI__builtin_is_constant_evaluated: if (!interp__builtin_is_constant_evaluated(S, OpPC, Frame, Call)) return false; diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 5d7a6e94f6e228..e3a88c069847b8 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -202,7 +202,7 @@ def CallVirt : Opcode { } def CallBI : Opcode { - let Args = [ArgFunction, ArgCallExpr]; + let Args = [ArgFunction, ArgCallExpr, ArgUint32]; } def CallPtr : Opcode { diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index 556efa65ae1181..902ab4aab10fb5 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -601,6 +601,19 @@ namespace std { }; } +/// Specialization for float, using operator new/delete. +namespace std { + using size_t = decltype(sizeof(0)); + template<> struct allocator { + constexpr float *allocate(size_t N) { + return (float*)operator new (sizeof(float) * N); + } + constexpr void deallocate(void *p) { + operator delete(p); + } + }; +} + namespace OperatorNewDelete { constexpr bool mismatched(int alloc_kind, int dealloc_kind) { @@ -696,6 +709,7 @@ namespace OperatorNewDelete { constexpr int no_deallocate_nullptr = (std::allocator().deallocate(nullptr), 1); // both-error {{constant expression}} \ // both-note {{in call}} + static_assert((std::allocator().deallocate(std::allocator().allocate(10)), 1) == 1); } #else