Skip to content

[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 1 commit into from
Sep 3, 2024

Conversation

tbaederr
Copy link
Contributor

@tbaederr tbaederr commented Sep 3, 2024

They are quite long and not templated.

They are quite long and not templated.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Sep 3, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 3, 2024

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

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 --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);

@tbaederr tbaederr merged commit f70ccda into llvm:main Sep 3, 2024
11 checks passed
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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants