-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][bytecode][NFC] Move Call ops into Interp.cpp #107104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
They are quite long and not templated.
@llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) ChangesThey are quite long and not templated. Full diff: https://github.com/llvm/llvm-project/pull/107104.diff 2 Files Affected:
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);
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
clang:frontend
Language frontend issues, e.g. anything involving "Sema"
clang
Clang issues not falling into any other category
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
They are quite long and not templated.