diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -69,6 +69,8 @@ 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/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- 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 @@ return this->bail(VD); } +template +bool ByteCodeExprGen::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(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(Ctx, P).compileFunc(FuncDecl)) + Func = *R; + } + assert(Func); + + QualType ReturnType = E->getCallReturnType(Ctx.getASTContext()); + Optional 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 +bool ByteCodeExprGen::VisitCXXDefaultArgExpr( + const CXXDefaultArgExpr *E) { + return this->visit(E->getExpr()); +} + template bool ByteCodeExprGen::VisitCXXBoolLiteralExpr( const CXXBoolLiteralExpr *E) { diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/clang/lib/AST/Interp/EvalEmitter.cpp @@ -102,6 +102,24 @@ return ReturnValue(S.Stk.pop(), Result); } +template +bool EvalEmitter::emitCall(const Function *Func, const SourceInfo &Info) { + + S.Current = + new InterpFrame(S, const_cast(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(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 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -62,7 +62,7 @@ /// 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 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -53,6 +53,27 @@ return true; } +template ::T> +static bool Call(InterpState &S, CodePtr &PC, const Function *Func) { + S.Current = + new InterpFrame(S, const_cast(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(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 @@ 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 --- a/clang/lib/AST/Interp/InterpFrame.h +++ b/clang/lib/AST/Interp/InterpFrame.h @@ -113,6 +113,7 @@ private: /// Returns an original argument from the stack. template const T &stackRef(unsigned Offset) { + assert(Args); return *reinterpret_cast(Args - ArgSize + Offset); } diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -42,7 +42,7 @@ 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 @@ // [] -> 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 --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -10,7 +10,7 @@ int *p = &a; return *p; } -//static_assert(getMinus5() == -5, "") TODO +static_assert(getMinus5() == -5, ""); constexpr int assign() { int m = 10; @@ -20,7 +20,7 @@ return m; } -//static_assert(assign() == 20, ""); TODO +static_assert(assign() == 20, ""); constexpr int pointerAssign() { @@ -31,7 +31,7 @@ return m; } -//static_assert(pointerAssign() == 12, ""); TODO +static_assert(pointerAssign() == 12, ""); constexpr int pointerDeref() { int m = 12; @@ -39,7 +39,7 @@ return *p; } -//static_assert(pointerDeref() == 12, ""); TODO +static_assert(pointerDeref() == 12, ""); constexpr int pointerAssign2() { int m = 10; @@ -52,4 +52,4 @@ 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 --- /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 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 --- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp +++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp @@ -120,6 +120,9 @@ 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 @@ 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";