[clang] [clang][bytecode][NFC] Move Call ops into Interp.cpp (PR #107104)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 3 06:15:08 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
They are quite long and not templated.
---
Full diff: https://github.com/llvm/llvm-project/pull/107104.diff
2 Files Affected:
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+235)
- (modified) clang/lib/AST/ByteCode/Interp.h (+11-237)
``````````diff
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);
``````````
</details>
https://github.com/llvm/llvm-project/pull/107104
More information about the cfe-commits
mailing list