forked from OSchip/llvm-project
[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
This commit is contained in:
parent
5777c05d16
commit
8e41e6a4ea
|
@ -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) {
|
||||
|
|
|
@ -69,6 +69,8 @@ public:
|
|||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
|
||||
/// 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.
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ public:
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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, "");
|
|
@ -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";
|
||||
|
|
Loading…
Reference in New Issue