https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/107104
They are quite long and not templated. >From f395bdadb094477b757a2c0789f902d56eb14df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Tue, 3 Sep 2024 15:11:57 +0200 Subject: [PATCH] [clang][bytecode][NFC] Move Call ops into Interp.cpp They are quite long and not templated. --- clang/lib/AST/ByteCode/Interp.cpp | 235 ++++++++++++++++++++++++++++ clang/lib/AST/ByteCode/Interp.h | 248 ++---------------------------- 2 files changed, 246 insertions(+), 237 deletions(-) diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 8f57afcb4dc120..6777ac150abf4f 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -986,6 +986,241 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, } } +bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, + uint32_t VarArgSize) { + if (Func->hasThisPointer()) { + size_t ArgSize = Func->getArgSize() + VarArgSize; + size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); + const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); + + // If the current function is a lambda static invoker and + // the function we're about to call is a lambda call operator, + // skip the CheckInvoke, since the ThisPtr is a null pointer + // anyway. + if (!(S.Current->getFunction() && + S.Current->getFunction()->isLambdaStaticInvoker() && + Func->isLambdaCallOperator())) { + if (!CheckInvoke(S, OpPC, ThisPtr)) + return false; + } + + if (S.checkingPotentialConstantExpression()) + return false; + } + + if (!CheckCallable(S, OpPC, Func)) + return false; + + if (!CheckCallDepth(S, OpPC)) + return false; + + auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize); + InterpFrame *FrameBefore = S.Current; + S.Current = NewFrame.get(); + + APValue CallResult; + // Note that we cannot assert(CallResult.hasValue()) here since + // Ret() above only sets the APValue if the curent frame doesn't + // have a caller set. + if (Interpret(S, CallResult)) { + NewFrame.release(); // Frame was delete'd already. + assert(S.Current == FrameBefore); + return true; + } + + // Interpreting the function failed somehow. Reset to + // previous state. + S.Current = FrameBefore; + return false; +} + +bool Call(InterpState &S, CodePtr OpPC, const Function *Func, + uint32_t VarArgSize) { + if (Func->hasThisPointer()) { + size_t ArgSize = Func->getArgSize() + VarArgSize; + size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); + + const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); + + // If the current function is a lambda static invoker and + // the function we're about to call is a lambda call operator, + // skip the CheckInvoke, since the ThisPtr is a null pointer + // anyway. + if (S.Current->getFunction() && + S.Current->getFunction()->isLambdaStaticInvoker() && + Func->isLambdaCallOperator()) { + assert(ThisPtr.isZero()); + } else { + if (!CheckInvoke(S, OpPC, ThisPtr)) + return false; + } + } + + if (!CheckCallable(S, OpPC, Func)) + return false; + + // FIXME: The isConstructor() check here is not always right. The current + // constant evaluator is somewhat inconsistent in when it allows a function + // call when checking for a constant expression. + if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() && + !Func->isConstructor()) + return false; + + if (!CheckCallDepth(S, OpPC)) + return false; + + auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize); + InterpFrame *FrameBefore = S.Current; + S.Current = NewFrame.get(); + + APValue CallResult; + // Note that we cannot assert(CallResult.hasValue()) here since + // Ret() above only sets the APValue if the curent frame doesn't + // have a caller set. + if (Interpret(S, CallResult)) { + NewFrame.release(); // Frame was delete'd already. + assert(S.Current == FrameBefore); + return true; + } + + // Interpreting the function failed somehow. Reset to + // previous state. + S.Current = FrameBefore; + return false; +} + +bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func, + uint32_t VarArgSize) { + assert(Func->hasThisPointer()); + assert(Func->isVirtual()); + size_t ArgSize = Func->getArgSize() + VarArgSize; + size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); + Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); + + const CXXRecordDecl *DynamicDecl = nullptr; + { + Pointer TypePtr = ThisPtr; + while (TypePtr.isBaseClass()) + TypePtr = TypePtr.getBase(); + + QualType DynamicType = TypePtr.getType(); + if (DynamicType->isPointerType() || DynamicType->isReferenceType()) + DynamicDecl = DynamicType->getPointeeCXXRecordDecl(); + else + DynamicDecl = DynamicType->getAsCXXRecordDecl(); + } + assert(DynamicDecl); + + const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl()); + const auto *InitialFunction = cast<CXXMethodDecl>(Func->getDecl()); + const CXXMethodDecl *Overrider = S.getContext().getOverridingFunction( + DynamicDecl, StaticDecl, InitialFunction); + + if (Overrider != InitialFunction) { + // DR1872: An instantiated virtual constexpr function can't be called in a + // constant expression (prior to C++20). We can still constant-fold such a + // call. + if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) { + const Expr *E = S.Current->getExpr(OpPC); + S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange(); + } + + Func = S.getContext().getOrCreateFunction(Overrider); + + const CXXRecordDecl *ThisFieldDecl = + ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl(); + if (Func->getParentDecl()->isDerivedFrom(ThisFieldDecl)) { + // If the function we call is further DOWN the hierarchy than the + // FieldDesc of our pointer, just go up the hierarchy of this field + // the furthest we can go. + while (ThisPtr.isBaseClass()) + ThisPtr = ThisPtr.getBase(); + } + } + + if (!Call(S, OpPC, Func, VarArgSize)) + return false; + + // Covariant return types. The return type of Overrider is a pointer + // or reference to a class type. + if (Overrider != InitialFunction && + Overrider->getReturnType()->isPointerOrReferenceType() && + InitialFunction->getReturnType()->isPointerOrReferenceType()) { + QualType OverriderPointeeType = + Overrider->getReturnType()->getPointeeType(); + QualType InitialPointeeType = + InitialFunction->getReturnType()->getPointeeType(); + // We've called Overrider above, but calling code expects us to return what + // InitialFunction returned. According to the rules for covariant return + // types, what InitialFunction returns needs to be a base class of what + // Overrider returns. So, we need to do an upcast here. + unsigned Offset = S.getContext().collectBaseOffset( + InitialPointeeType->getAsRecordDecl(), + OverriderPointeeType->getAsRecordDecl()); + return GetPtrBasePop(S, OpPC, Offset); + } + + return true; +} + +bool CallBI(InterpState &S, CodePtr &PC, const Function *Func, + const CallExpr *CE) { + auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC); + + InterpFrame *FrameBefore = S.Current; + S.Current = NewFrame.get(); + + if (InterpretBuiltin(S, PC, Func, CE)) { + NewFrame.release(); + return true; + } + S.Current = FrameBefore; + return false; +} + +bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, + const CallExpr *CE) { + const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>(); + + const Function *F = FuncPtr.getFunction(); + if (!F) { + const auto *E = cast<CallExpr>(S.Current->getExpr(OpPC)); + S.FFDiag(E, diag::note_constexpr_null_callee) + << const_cast<Expr *>(E->getCallee()) << E->getSourceRange(); + return false; + } + + if (!FuncPtr.isValid() || !F->getDecl()) + return Invalid(S, OpPC); + + assert(F); + + // This happens when the call expression has been cast to + // something else, but we don't support that. + if (S.Ctx.classify(F->getDecl()->getReturnType()) != + S.Ctx.classify(CE->getType())) + return false; + + // Check argument nullability state. + if (F->hasNonNullAttr()) { + if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize)) + return false; + } + + assert(ArgSize >= F->getWrittenArgSize()); + uint32_t VarArgSize = ArgSize - F->getWrittenArgSize(); + + // We need to do this explicitly here since we don't have the necessary + // information to do it automatically. + if (F->isThisPointerExplicit()) + VarArgSize -= align(primSize(PT_Ptr)); + + if (F->isVirtual()) + return CallVirt(S, OpPC, F, VarArgSize); + + return Call(S, OpPC, F, VarArgSize); +} + bool Interpret(InterpState &S, APValue &Result) { // The current stack frame when we started Interpret(). // This is being used by the ops to determine wheter diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index e1d880060a0bac..be900769f25845 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -147,6 +147,17 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC, /// Copy the contents of Src into Dest. bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest); +bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, + uint32_t VarArgSize); +bool Call(InterpState &S, CodePtr OpPC, const Function *Func, + uint32_t VarArgSize); +bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func, + uint32_t VarArgSize); +bool CallBI(InterpState &S, CodePtr &PC, const Function *Func, + const CallExpr *CE); +bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, + const CallExpr *CE); + /// Checks if the shift operation is legal. template <typename LT, typename RT> bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS, @@ -2593,243 +2604,6 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) { return false; } -inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, - uint32_t VarArgSize) { - if (Func->hasThisPointer()) { - size_t ArgSize = Func->getArgSize() + VarArgSize; - size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); - const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); - - // If the current function is a lambda static invoker and - // the function we're about to call is a lambda call operator, - // skip the CheckInvoke, since the ThisPtr is a null pointer - // anyway. - if (!(S.Current->getFunction() && - S.Current->getFunction()->isLambdaStaticInvoker() && - Func->isLambdaCallOperator())) { - if (!CheckInvoke(S, OpPC, ThisPtr)) - return false; - } - - if (S.checkingPotentialConstantExpression()) - return false; - } - - if (!CheckCallable(S, OpPC, Func)) - return false; - - if (!CheckCallDepth(S, OpPC)) - return false; - - auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize); - InterpFrame *FrameBefore = S.Current; - S.Current = NewFrame.get(); - - APValue CallResult; - // Note that we cannot assert(CallResult.hasValue()) here since - // Ret() above only sets the APValue if the curent frame doesn't - // have a caller set. - if (Interpret(S, CallResult)) { - NewFrame.release(); // Frame was delete'd already. - assert(S.Current == FrameBefore); - return true; - } - - // Interpreting the function failed somehow. Reset to - // previous state. - S.Current = FrameBefore; - return false; - - return false; -} - -inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func, - uint32_t VarArgSize) { - if (Func->hasThisPointer()) { - size_t ArgSize = Func->getArgSize() + VarArgSize; - size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); - - const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); - - // If the current function is a lambda static invoker and - // the function we're about to call is a lambda call operator, - // skip the CheckInvoke, since the ThisPtr is a null pointer - // anyway. - if (S.Current->getFunction() && - S.Current->getFunction()->isLambdaStaticInvoker() && - Func->isLambdaCallOperator()) { - assert(ThisPtr.isZero()); - } else { - if (!CheckInvoke(S, OpPC, ThisPtr)) - return false; - } - } - - if (!CheckCallable(S, OpPC, Func)) - return false; - - // FIXME: The isConstructor() check here is not always right. The current - // constant evaluator is somewhat inconsistent in when it allows a function - // call when checking for a constant expression. - if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() && - !Func->isConstructor()) - return false; - - if (!CheckCallDepth(S, OpPC)) - return false; - - auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize); - InterpFrame *FrameBefore = S.Current; - S.Current = NewFrame.get(); - - APValue CallResult; - // Note that we cannot assert(CallResult.hasValue()) here since - // Ret() above only sets the APValue if the curent frame doesn't - // have a caller set. - if (Interpret(S, CallResult)) { - NewFrame.release(); // Frame was delete'd already. - assert(S.Current == FrameBefore); - return true; - } - - // Interpreting the function failed somehow. Reset to - // previous state. - S.Current = FrameBefore; - return false; -} - -inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func, - uint32_t VarArgSize) { - assert(Func->hasThisPointer()); - assert(Func->isVirtual()); - size_t ArgSize = Func->getArgSize() + VarArgSize; - size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0); - Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset); - - const CXXRecordDecl *DynamicDecl = nullptr; - { - Pointer TypePtr = ThisPtr; - while (TypePtr.isBaseClass()) - TypePtr = TypePtr.getBase(); - - QualType DynamicType = TypePtr.getType(); - if (DynamicType->isPointerType() || DynamicType->isReferenceType()) - DynamicDecl = DynamicType->getPointeeCXXRecordDecl(); - else - DynamicDecl = DynamicType->getAsCXXRecordDecl(); - } - assert(DynamicDecl); - - const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl()); - const auto *InitialFunction = cast<CXXMethodDecl>(Func->getDecl()); - const CXXMethodDecl *Overrider = S.getContext().getOverridingFunction( - DynamicDecl, StaticDecl, InitialFunction); - - if (Overrider != InitialFunction) { - // DR1872: An instantiated virtual constexpr function can't be called in a - // constant expression (prior to C++20). We can still constant-fold such a - // call. - if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) { - const Expr *E = S.Current->getExpr(OpPC); - S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange(); - } - - Func = S.getContext().getOrCreateFunction(Overrider); - - const CXXRecordDecl *ThisFieldDecl = - ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl(); - if (Func->getParentDecl()->isDerivedFrom(ThisFieldDecl)) { - // If the function we call is further DOWN the hierarchy than the - // FieldDesc of our pointer, just go up the hierarchy of this field - // the furthest we can go. - while (ThisPtr.isBaseClass()) - ThisPtr = ThisPtr.getBase(); - } - } - - if (!Call(S, OpPC, Func, VarArgSize)) - return false; - - // Covariant return types. The return type of Overrider is a pointer - // or reference to a class type. - if (Overrider != InitialFunction && - Overrider->getReturnType()->isPointerOrReferenceType() && - InitialFunction->getReturnType()->isPointerOrReferenceType()) { - QualType OverriderPointeeType = - Overrider->getReturnType()->getPointeeType(); - QualType InitialPointeeType = - InitialFunction->getReturnType()->getPointeeType(); - // We've called Overrider above, but calling code expects us to return what - // InitialFunction returned. According to the rules for covariant return - // types, what InitialFunction returns needs to be a base class of what - // Overrider returns. So, we need to do an upcast here. - unsigned Offset = S.getContext().collectBaseOffset( - InitialPointeeType->getAsRecordDecl(), - OverriderPointeeType->getAsRecordDecl()); - return GetPtrBasePop(S, OpPC, Offset); - } - - return true; -} - -inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func, - const CallExpr *CE) { - auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC); - - InterpFrame *FrameBefore = S.Current; - S.Current = NewFrame.get(); - - if (InterpretBuiltin(S, PC, Func, CE)) { - NewFrame.release(); - return true; - } - S.Current = FrameBefore; - return false; -} - -inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, - const CallExpr *CE) { - const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>(); - - const Function *F = FuncPtr.getFunction(); - if (!F) { - const auto *E = cast<CallExpr>(S.Current->getExpr(OpPC)); - S.FFDiag(E, diag::note_constexpr_null_callee) - << const_cast<Expr *>(E->getCallee()) << E->getSourceRange(); - return false; - } - - if (!FuncPtr.isValid() || !F->getDecl()) - return Invalid(S, OpPC); - - assert(F); - - // This happens when the call expression has been cast to - // something else, but we don't support that. - if (S.Ctx.classify(F->getDecl()->getReturnType()) != - S.Ctx.classify(CE->getType())) - return false; - - // Check argument nullability state. - if (F->hasNonNullAttr()) { - if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize)) - return false; - } - - assert(ArgSize >= F->getWrittenArgSize()); - uint32_t VarArgSize = ArgSize - F->getWrittenArgSize(); - - // We need to do this explicitly here since we don't have the necessary - // information to do it automatically. - if (F->isThisPointerExplicit()) - VarArgSize -= align(primSize(PT_Ptr)); - - if (F->isVirtual()) - return CallVirt(S, OpPC, F, VarArgSize); - - return Call(S, OpPC, F, VarArgSize); -} - inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) { assert(Func); S.Stk.push<FunctionPointer>(Func); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits