#include "ByteCodeExprGen.h"
#include "ByteCodeEmitter.h"
#include "ByteCodeGenError.h"
+#include "ByteCodeStmtGen.h"
#include "Context.h"
#include "Function.h"
#include "PrimType.h"
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) {
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);
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) {
/// 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.
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--;
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();
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);
}
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 *"; }
// [] -> 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
//===----------------------------------------------------------------------===//
int *p = &a;
return *p;
}
-//static_assert(getMinus5() == -5, "") TODO
+static_assert(getMinus5() == -5, "");
constexpr int assign() {
int m = 10;
return m;
}
-//static_assert(assign() == 20, ""); TODO
+static_assert(assign() == 20, "");
constexpr int pointerAssign() {
return m;
}
-//static_assert(pointerAssign() == 12, ""); TODO
+static_assert(pointerAssign() == 12, "");
constexpr int pointerDeref() {
int m = 12;
return *p;
}
-//static_assert(pointerDeref() == 12, ""); TODO
+static_assert(pointerDeref() == 12, "");
constexpr int pointerAssign2() {
int m = 10;
return v;
}
-//static_assert(pointerAssign2() == 12, ""); TODO
+static_assert(pointerAssign2() == 12, "");
--- /dev/null
+// 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, "");
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;
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";