[clang] 8e41e6a - [clang][Interp] Implement function calls

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 7 22:31:49 PDT 2022


Author: Timm Bäder
Date: 2022-09-08T07:31:07+02:00
New Revision: 8e41e6a4eafa2b667ec37ece33a85493fe0156c2

URL: https://github.com/llvm/llvm-project/commit/8e41e6a4eafa2b667ec37ece33a85493fe0156c2
DIFF: https://github.com/llvm/llvm-project/commit/8e41e6a4eafa2b667ec37ece33a85493fe0156c2.diff

LOG: [clang][Interp] Implement function calls

Add Call() and CallVoid() ops and use them to call functions. Only
FunctionDecls are supported for now.

Differential Revision: https://reviews.llvm.org/D132286

Added: 
    clang/test/AST/Interp/functions.cpp

Modified: 
    clang/lib/AST/Interp/ByteCodeExprGen.cpp
    clang/lib/AST/Interp/ByteCodeExprGen.h
    clang/lib/AST/Interp/EvalEmitter.cpp
    clang/lib/AST/Interp/Function.h
    clang/lib/AST/Interp/Interp.cpp
    clang/lib/AST/Interp/InterpFrame.h
    clang/lib/AST/Interp/Opcodes.td
    clang/test/AST/Interp/cxx20.cpp
    clang/utils/TableGen/ClangOpcodesEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 03f877197d16..18f3341439ee 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -9,6 +9,7 @@
 #include "ByteCodeExprGen.h"
 #include "ByteCodeEmitter.h"
 #include "ByteCodeGenError.h"
+#include "ByteCodeStmtGen.h"
 #include "Context.h"
 #include "Function.h"
 #include "PrimType.h"
@@ -593,6 +594,52 @@ bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
   return this->bail(VD);
 }
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
+  assert(!E->getBuiltinCallee() && "Builtin functions aren't supported yet");
+
+  const Decl *Callee = E->getCalleeDecl();
+  if (const auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(Callee)) {
+    const Function *Func = P.getFunction(FuncDecl);
+
+    // Templated functions might not have been compiled yet, so do it now.
+    if (!Func) {
+      if (auto R =
+              ByteCodeStmtGen<ByteCodeEmitter>(Ctx, P).compileFunc(FuncDecl))
+        Func = *R;
+    }
+    assert(Func);
+
+    QualType ReturnType = E->getCallReturnType(Ctx.getASTContext());
+    Optional<PrimType> T = classify(ReturnType);
+
+    if (T || ReturnType->isVoidType()) {
+      // Put arguments on the stack.
+      for (const auto *Arg : E->arguments()) {
+        if (!this->visit(Arg))
+          return false;
+      }
+
+      if (T)
+        return this->emitCall(*T, Func, E);
+      return this->emitCallVoid(Func, E);
+    } else {
+      assert(false && "Can't classify function return type");
+    }
+
+  } else {
+    assert(false && "We don't support non-FunctionDecl callees right now.");
+  }
+
+  return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXDefaultArgExpr(
+    const CXXDefaultArgExpr *E) {
+  return this->visit(E->getExpr());
+}
+
 template <class Emitter>
 bool ByteCodeExprGen<Emitter>::VisitCXXBoolLiteralExpr(
     const CXXBoolLiteralExpr *E) {

diff  --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index f0459b55c8ef..f603f436f3c7 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -69,6 +69,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
   bool VisitIntegerLiteral(const IntegerLiteral *E);
   bool VisitParenExpr(const ParenExpr *E);
   bool VisitBinaryOperator(const BinaryOperator *E);
+  bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E);
+  bool VisitCallExpr(const CallExpr *E);
   bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E);
   bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E);
   bool VisitUnaryOperator(const UnaryOperator *E);

diff  --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index 22e8695b9211..3cc7ab0257d6 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -102,6 +102,24 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
   return ReturnValue<T>(S.Stk.pop<T>(), Result);
 }
 
+template <PrimType OpType>
+bool EvalEmitter::emitCall(const Function *Func, const SourceInfo &Info) {
+
+  S.Current =
+      new InterpFrame(S, const_cast<Function *>(Func), S.Current, {}, {});
+  // Result of call will be on the stack and needs to be handled by the caller.
+  return Interpret(S, Result);
+}
+
+bool EvalEmitter::emitCallVoid(const Function *Func, const SourceInfo &Info) {
+  APValue VoidResult;
+  S.Current =
+      new InterpFrame(S, const_cast<Function *>(Func), S.Current, {}, {});
+  bool Success = Interpret(S, VoidResult);
+  assert(VoidResult.isAbsent());
+  return Success;
+}
+
 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; }
 
 bool EvalEmitter::emitRetValue(const SourceInfo &Info) {

diff  --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h
index fa850f76c026..b009167c1bd6 100644
--- a/clang/lib/AST/Interp/Function.h
+++ b/clang/lib/AST/Interp/Function.h
@@ -62,7 +62,7 @@ class Function {
 
   /// Returns the size of the function's local stack.
   unsigned getFrameSize() const { return FrameSize; }
-  /// Returns the size of the argument stackx
+  /// Returns the size of the argument stack.
   unsigned getArgSize() const { return ArgSize; }
 
   /// Returns a pointer to the start of the code.

diff  --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 7b64891ee68a..1a1f05253519 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -53,6 +53,27 @@ static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
   return true;
 }
 
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+static bool Call(InterpState &S, CodePtr &PC, const Function *Func) {
+  S.Current =
+      new InterpFrame(S, const_cast<Function *>(Func), S.Current, PC, {});
+  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.
+  return Interpret(S, CallResult);
+}
+
+static bool CallVoid(InterpState &S, CodePtr &PC, const Function *Func) {
+  APValue VoidResult;
+  S.Current =
+      new InterpFrame(S, const_cast<Function *>(Func), S.Current, PC, {});
+  bool Success = Interpret(S, VoidResult);
+  assert(VoidResult.isAbsent());
+
+  return Success;
+}
+
 static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
   S.CallStackDepth--;
 
@@ -398,7 +419,13 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
   S.Note(MD->getLocation(), diag::note_declared_at);
   return false;
 }
+
 bool Interpret(InterpState &S, APValue &Result) {
+  // The current stack frame when we started Interpret().
+  // This is being used by the ops to determine wheter
+  // to return from this function and thus terminate
+  // interpretation.
+  const InterpFrame *StartFrame = S.Current;
   assert(!S.Current->isRoot());
   CodePtr PC = S.Current->getPC();
 

diff  --git a/clang/lib/AST/Interp/InterpFrame.h b/clang/lib/AST/Interp/InterpFrame.h
index 304e2ad66537..3769c9a5f5f1 100644
--- a/clang/lib/AST/Interp/InterpFrame.h
+++ b/clang/lib/AST/Interp/InterpFrame.h
@@ -113,6 +113,7 @@ class InterpFrame final : public Frame {
 private:
   /// Returns an original argument from the stack.
   template <typename T> const T &stackRef(unsigned Offset) {
+    assert(Args);
     return *reinterpret_cast<const T *>(Args - ArgSize + Offset);
   }
 

diff  --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 971446b06f53..8bc072c53d6b 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -42,7 +42,7 @@ def ArgSint64 : ArgType { let Name = "int64_t"; }
 def ArgUint64 : ArgType { let Name = "uint64_t"; }
 def ArgBool : ArgType { let Name = "bool"; }
 
-def ArgFunction : ArgType { let Name = "Function *"; }
+def ArgFunction : ArgType { let Name = "const Function *"; }
 def ArgRecord : ArgType { let Name = "Record *"; }
 
 def ArgSema : ArgType { let Name = "const fltSemantics *"; }
@@ -153,6 +153,22 @@ def RetValue : Opcode {
 // [] -> EXIT
 def NoRet : Opcode {}
 
+
+def Call : Opcode {
+  let Args = [ArgFunction];
+  let Types = [AllTypeClass];
+  let ChangesPC = 1;
+  let HasCustomEval = 1;
+  let HasGroup = 1;
+}
+
+def CallVoid : Opcode {
+  let Args = [ArgFunction];
+  let Types = [];
+  let ChangesPC = 1;
+  let HasCustomEval = 1;
+}
+
 //===----------------------------------------------------------------------===//
 // Frame management
 //===----------------------------------------------------------------------===//

diff  --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp
index 30de02965833..1e43b472793a 100644
--- a/clang/test/AST/Interp/cxx20.cpp
+++ b/clang/test/AST/Interp/cxx20.cpp
@@ -10,7 +10,7 @@ constexpr int getMinus5() {
   int *p = &a;
   return *p;
 }
-//static_assert(getMinus5() == -5, "") TODO
+static_assert(getMinus5() == -5, "");
 
 constexpr int assign() {
   int m = 10;
@@ -20,7 +20,7 @@ constexpr int assign() {
 
   return m;
 }
-//static_assert(assign() == 20, "");  TODO
+static_assert(assign() == 20, "");
 
 
 constexpr int pointerAssign() {
@@ -31,7 +31,7 @@ constexpr int pointerAssign() {
 
   return m;
 }
-//static_assert(pointerAssign() == 12, "");  TODO
+static_assert(pointerAssign() == 12, "");
 
 constexpr int pointerDeref() {
   int m = 12;
@@ -39,7 +39,7 @@ constexpr int pointerDeref() {
 
   return *p;
 }
-//static_assert(pointerDeref() == 12, ""); TODO
+static_assert(pointerDeref() == 12, "");
 
 constexpr int pointerAssign2() {
   int m = 10;
@@ -52,4 +52,4 @@ constexpr int pointerAssign2() {
 
   return v;
 }
-//static_assert(pointerAssign2() == 12, ""); TODO
+static_assert(pointerAssign2() == 12, "");

diff  --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp
new file mode 100644
index 000000000000..a0e0a14a03c9
--- /dev/null
+++ b/clang/test/AST/Interp/functions.cpp
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -verify=ref %s
+
+// expected-no-diagnostics
+// ref-no-diagnostics
+
+constexpr void doNothing() {}
+constexpr int gimme5() {
+  doNothing();
+  return 5;
+}
+static_assert(gimme5() == 5, "");
+
+
+template<typename T> constexpr T identity(T t) { return t; }
+static_assert(identity(true), "");
+static_assert(identity(true), ""); /// Compiled bytecode should be cached
+static_assert(!identity(false), "");
+
+constexpr auto add(int a, int b) -> int {
+  return identity(a) + identity(b);
+}
+
+constexpr int sub(int a, int b) {
+  return a - b;
+}
+static_assert(sub(5, 2) == 3, "");
+static_assert(sub(0, 5) == -5, "");
+
+constexpr int norm(int n) {
+  if (n >= 0) {
+    return identity(n);
+  }
+  return -identity(n);
+}
+static_assert(norm(5) == norm(-5), "");
+
+constexpr int square(int n) {
+  return norm(n) * norm(n);
+}
+static_assert(square(2) == 4, "");
+
+constexpr int add_second(int a, int b, bool doAdd = true) {
+  if (doAdd)
+    return a + b;
+  return a;
+}
+static_assert(add_second(10, 3, true) == 13, "");
+static_assert(add_second(10, 3) == 13, "");
+static_assert(add_second(300, -20, false) == 300, "");
+
+
+constexpr int sub(int a, int b, int c) {
+  return a - b - c;
+}
+static_assert(sub(10, 8, 2) == 0, "");
+
+
+constexpr int recursion(int i) {
+  doNothing();
+  i = i - 1;
+  if (i == 0)
+    return identity(0);
+
+  return recursion(i);
+}
+static_assert(recursion(10) == 0, "");

diff  --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index 8081096633d9..aa012233c46e 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -120,6 +120,9 @@ void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N, Record *R) {
 
     OS << "case OP_" << ID << ": {\n";
 
+    if (CanReturn)
+      OS << "  bool DoReturn = (S.Current == StartFrame);\n";
+
     // Emit calls to read arguments.
     for (size_t I = 0, N = Args.size(); I < N; ++I) {
       OS << "  auto V" << I;
@@ -146,6 +149,9 @@ void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N, Record *R) {
     if (CanReturn) {
       OS << "  if (!S.Current || S.Current->isRoot())\n";
       OS << "    return true;\n";
+
+      OS << "  if (DoReturn)\n";
+      OS << "    return true;\n";
     }
 
     OS << "  continue;\n";


        


More information about the cfe-commits mailing list