tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, shafik, erichkeane, MaskRay, rsmith,
nand, nandor.
Herald added a subscriber: StephenFan.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
Hi,
I wanted to maybe work on the new experimental constant interpreter in clang. I
took last week to get to know the code a little and implement some basic
functionality.
New in this patch are:
- Simple function calls
- array initializers and subscript expressions
- pointer reference and dereference
- local and global variables
Some tests for the functionality is in `test/AST/Interp/interp.cpp`.
As far as I know the interpreter has been abandoned quite some time ago, so I
was just looking for some basic feedback about whether this is worth it to
pursue. If the feedback is positive, I will continue working on it.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D131888
Files:
clang/lib/AST/Interp/Boolean.h
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/ByteCodeStmtGen.h
clang/lib/AST/Interp/Context.h
clang/lib/AST/Interp/Descriptor.h
clang/lib/AST/Interp/Disasm.cpp
clang/lib/AST/Interp/EvalEmitter.cpp
clang/lib/AST/Interp/Function.cpp
clang/lib/AST/Interp/Function.h
clang/lib/AST/Interp/Integral.h
clang/lib/AST/Interp/Interp.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/InterpBlock.h
clang/lib/AST/Interp/InterpFrame.cpp
clang/lib/AST/Interp/InterpStack.h
clang/lib/AST/Interp/InterpState.h
clang/lib/AST/Interp/Opcodes.td
clang/lib/AST/Interp/Pointer.h
clang/lib/AST/Interp/PrimType.h
clang/lib/AST/Interp/Source.h
clang/test/AST/Interp/interp.cpp
Index: clang/test/AST/Interp/interp.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/Interp/interp.cpp
@@ -0,0 +1,121 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fexperimental-new-constant-interpreter %s -verify
+
+// expected-no-diagnostics
+
+constexpr bool foo() { return true; }
+constexpr bool bar() { return false; }
+static_assert(foo());
+
+static_assert(5);
+static_assert(!0);
+
+///------------------------------------------------------------------
+/// Unary inv
+static_assert(!false);
+static_assert(!!true);
+static_assert(!bar());
+static_assert(!!foo());
+
+///------------------------------------------------------------------
+/// Return values
+constexpr int get4() { return 4; }
+static_assert(get4() != 5);
+
+
+///------------------------------------------------------------------
+/// Functions calling other functions
+constexpr bool getTrue() { return foo(); }
+constexpr bool getTrue2() { return !bar(); }
+static_assert(getTrue());
+static_assert(getTrue2());
+
+//------------------------------------------------------------------
+// Parameters
+// TODO:
+//template<typename T>
+//constexpr T identity(T t) { return t; }
+//static_assert(identity(true));
+constexpr bool bool_identity(bool b) { return b; }
+static_assert(bool_identity(true));
+static_assert(!bool_identity(false));
+
+constexpr bool inv(bool b) { return !b;}
+static_assert(inv(false));
+
+constexpr int add(int a, int b) { return a + b; }
+static_assert(add(1,2) == 3);
+
+constexpr int sub(int a, int b) { return a - b; }
+static_assert(sub(0, 2) == -2);
+
+
+
+///------------------------------------------------------------------
+/// Global Variables
+constexpr bool t = true;
+constexpr bool f = false;
+constexpr int n = 13;
+constexpr int zero = 0;
+static_assert(t);
+static_assert(!f);
+static_assert(n);
+static_assert(!zero);
+
+
+///------------------------------------------------------------------
+/// Local variables
+constexpr int assign() {
+ int f = 20;
+ f = 100;
+ return f;
+}
+static_assert(assign() == 100);
+
+
+
+///------------------------------------------------------------------
+/// Pointers
+
+constexpr const int *ptr = nullptr;
+static_assert(ptr == nullptr);
+constexpr const int *const* ptr2 = &ptr;
+static_assert(ptr2 != nullptr);
+
+constexpr const int *zomg = *ptr2;
+static_assert(zomg == nullptr);
+
+constexpr int five = 5;
+constexpr const int *pointer_to_5 = &five;
+constexpr const int five_from_pointer = *pointer_to_5;
+static_assert(five_from_pointer == 5);
+
+
+
+
+
+///------------------------------------------------------------------
+/// If Statements
+constexpr int cond(bool b) {
+ if (b) {
+ return 5;
+ } else {
+ return 3;
+ }
+}
+static_assert(cond(true) == 5);
+static_assert(cond(false) == 3);
+
+
+///------------------------------------------------------------------
+/// Arrays
+constexpr int intarr[] = { 1, 2, 3, 4 };
+static_assert(intarr[0] == 1);
+static_assert(intarr[1] == 2);
+static_assert(intarr[2] == 3);
+static_assert(intarr[3] == 4);
+constexpr int someArr() {
+ int v[] = { 1, 2, 3, 4};
+ return v[0] + v[1] + v[2];
+}
+static_assert(someArr() == 6);
+
Index: clang/lib/AST/Interp/Source.h
===================================================================
--- clang/lib/AST/Interp/Source.h
+++ clang/lib/AST/Interp/Source.h
@@ -22,7 +22,7 @@
class Function;
/// Pointer into the code segment.
-class CodePtr {
+class CodePtr final {
public:
CodePtr() : Ptr(nullptr) {}
@@ -43,6 +43,8 @@
bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; }
+ operator bool() const { return Ptr; }
+
/// Reads data and advances the pointer.
template <typename T> std::enable_if_t<!std::is_pointer<T>::value, T> read() {
using namespace llvm::support;
@@ -63,7 +65,7 @@
};
/// Describes the statement/declaration an opcode was generated from.
-class SourceInfo {
+class SourceInfo final {
public:
SourceInfo() {}
SourceInfo(const Stmt *E) : Source(E) {}
Index: clang/lib/AST/Interp/PrimType.h
===================================================================
--- clang/lib/AST/Interp/PrimType.h
+++ clang/lib/AST/Interp/PrimType.h
@@ -25,16 +25,16 @@
/// Enumeration of the primitive types of the VM.
enum PrimType : unsigned {
- PT_Sint8,
- PT_Uint8,
- PT_Sint16,
- PT_Uint16,
- PT_Sint32,
- PT_Uint32,
- PT_Sint64,
- PT_Uint64,
- PT_Bool,
- PT_Ptr,
+ PT_Sint8 = 0,
+ PT_Uint8 = 1,
+ PT_Sint16 = 2,
+ PT_Uint16 = 3,
+ PT_Sint32 = 4,
+ PT_Uint32 = 5,
+ PT_Sint64 = 6,
+ PT_Uint64 = 7,
+ PT_Bool = 8,
+ PT_Ptr = 9,
};
/// Mapping from primitive types to their representation.
Index: clang/lib/AST/Interp/Pointer.h
===================================================================
--- clang/lib/AST/Interp/Pointer.h
+++ clang/lib/AST/Interp/Pointer.h
@@ -306,6 +306,11 @@
OS << "}";
}
+ void dump() const {
+ print(llvm::errs());
+ llvm::errs() << '\n';
+ }
+
private:
friend class Block;
friend class DeadBlock;
Index: clang/lib/AST/Interp/Opcodes.td
===================================================================
--- clang/lib/AST/Interp/Opcodes.td
+++ 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 *"; }
@@ -73,6 +73,10 @@
let Types = [Ptr];
}
+def BoolTypeClass : TypeClass {
+ let Types = [Bool];
+}
+
def AllTypeClass : TypeClass {
let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types);
}
@@ -149,6 +153,17 @@
// [] -> EXIT
def NoRet : Opcode {}
+
+def Call : Opcode {
+ let Args = [ArgFunction];
+ let Types = [AllTypeClass];
+ let ChangesPC = 1;
+ let CanReturn = 1;
+ let HasCustomEval = 1;
+ let HasGroup = 1;
+}
+
+
//===----------------------------------------------------------------------===//
// Frame management
//===----------------------------------------------------------------------===//
@@ -383,6 +398,25 @@
def Add : AluOpcode;
def Mul : AluOpcode;
+
+//===----------------------------------------------------------------------===//
+// Unary operators.
+//===----------------------------------------------------------------------===//
+
+
+
+// [Real] -> [Real]
+def Inv: Opcode {
+ let Types = [BoolTypeClass];
+ let HasGroup = 1;
+}
+
+// [Real] -> [Real]
+def Neg: Opcode {
+ let Types = [AluTypeClass];
+ let HasGroup = 1;
+}
+
//===----------------------------------------------------------------------===//
// Comparison opcodes.
//===----------------------------------------------------------------------===//
Index: clang/lib/AST/Interp/InterpState.h
===================================================================
--- clang/lib/AST/Interp/InterpState.h
+++ clang/lib/AST/Interp/InterpState.h
@@ -85,9 +85,10 @@
return M ? M->getSource(F, PC) : F->getSource(PC);
}
-private:
/// AST Walker state.
State &Parent;
+
+private:
/// Dead block chain.
DeadBlock *DeadBlocks = nullptr;
/// Reference to the offset-source mapping.
Index: clang/lib/AST/Interp/InterpStack.h
===================================================================
--- clang/lib/AST/Interp/InterpStack.h
+++ clang/lib/AST/Interp/InterpStack.h
@@ -13,6 +13,8 @@
#ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
#define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
+#include "Pointer.h"
+#include "llvm/Support/raw_ostream.h"
#include <memory>
namespace clang {
@@ -58,9 +60,21 @@
/// Returns the size of the stack in bytes.
size_t size() const { return StackSize; }
+ /// Return whether the stack is empty.
+ bool empty() const { return StackSize == 0; }
+
/// Clears the stack without calling any destructors.
void clear();
+ void dump() {
+ llvm::errs() << "InterpStack(" << size() << "):\n";
+ // Try to read everything as sint32.
+ while (!empty()) {
+ auto V = this->pop<int32_t>();
+ llvm::errs() << V << "\n";
+ }
+ }
+
private:
/// All stack slots are aligned to the native pointer alignment for storage.
/// The size of an object is rounded up to a pointer alignment multiple.
Index: clang/lib/AST/Interp/InterpFrame.cpp
===================================================================
--- clang/lib/AST/Interp/InterpFrame.cpp
+++ clang/lib/AST/Interp/InterpFrame.cpp
@@ -133,7 +133,7 @@
if (I + 1 != N)
OS << ", ";
}
- OS << ")";
+ OS << ")\n";
}
Frame *InterpFrame::getCaller() const {
Index: clang/lib/AST/Interp/InterpBlock.h
===================================================================
--- clang/lib/AST/Interp/InterpBlock.h
+++ clang/lib/AST/Interp/InterpBlock.h
@@ -73,6 +73,23 @@
/*isActive=*/true, Desc);
}
+ void print(llvm::raw_ostream &OS) const {
+ OS << "Block {\n";
+ OS << " hasPointers: " << hasPointers() << "\n";
+ OS << " isExtern: " << isExtern() << "\n";
+ OS << " isStatic: " << isStatic() << "\n";
+ OS << " isArray: " << getDescriptor()->isArray() << "\n";
+ if (getDescriptor()->isArray()) {
+ OS << " ElemSize: " << getDescriptor()->getElemSize() << "\n";
+ OS << " NumElems: " << getDescriptor()->getNumElems() << "\n";
+ }
+ OS << "}";
+ }
+ void dump() const {
+ print(llvm::errs());
+ llvm::errs() << '\n';
+ }
+
protected:
friend class Pointer;
friend class DeadBlock;
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -19,7 +19,6 @@
#include "InterpState.h"
#include "Opcode.h"
#include "PrimType.h"
-#include "Program.h"
#include "State.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
@@ -154,6 +153,36 @@
return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
}
+//===----------------------------------------------------------------------===//
+// Inv
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inv(InterpState &S, CodePtr OpPC) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const T &Val = S.Stk.pop<T>();
+ const unsigned Bits = Val.bitWidth();
+ Boolean R;
+ Boolean::inv(BoolT::from(Val, Bits), &R);
+
+ S.Stk.push<BoolT>(R);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Neg
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Neg(InterpState &S, CodePtr OpPC) {
+ const T &Val = S.Stk.pop<T>();
+ T Result;
+ T::neg(Val, &Result);
+
+ S.Stk.push<T>(Result);
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// EQ, NE, GT, GE, LT, LE
//===----------------------------------------------------------------------===//
@@ -412,7 +441,7 @@
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
- // TODO: emit warning.
+ llvm_unreachable("Can't set a global");
return false;
}
@@ -632,6 +661,9 @@
return true;
}
+/// Pops the value to store from the stack.
+/// Writes it at the pointer that's next on the stack.
+/// Does NOT pop the pointer from the stack.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Store(InterpState &S, CodePtr OpPC) {
const T &Value = S.Stk.pop<T>();
@@ -956,13 +988,13 @@
template <typename T>
inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(InterpState &S,
- CodePtr OpPC) {
+ CodePtr &OpPC) {
return OpPC.read<T>();
}
template <typename T>
inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(InterpState &S,
- CodePtr OpPC) {
+ CodePtr &OpPC) {
uint32_t ID = OpPC.read<uint32_t>();
return reinterpret_cast<T>(S.P.getNativePointer(ID));
}
Index: clang/lib/AST/Interp/Interp.cpp
===================================================================
--- clang/lib/AST/Interp/Interp.cpp
+++ clang/lib/AST/Interp/Interp.cpp
@@ -53,6 +53,25 @@
return true;
}
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+static bool Call(InterpState &S, CodePtr &PC, APValue &Result,
+ const Function *Func) {
+ InterpStack Stack;
+ InterpState State(S, S.P, Stack, S.Ctx);
+ State.Current =
+ new InterpFrame(State, const_cast<Function *>(Func), nullptr, {}, {});
+ if (Interpret(State, Result)) {
+ // TODO: Support non-integer return values
+ assert(Result.isInt());
+ assert(Result.getInt().getZExtValue() == 0 ||
+ Result.getInt().getZExtValue() == 1);
+ S.Stk.push<bool>(Result.getInt().getZExtValue());
+ return true;
+ }
+
+ return false;
+}
+
static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
S.CallStackDepth--;
@@ -398,9 +417,15 @@
S.Note(MD->getLocation(), diag::note_declared_at);
return false;
}
+
bool Interpret(InterpState &S, APValue &Result) {
+ assert(!S.Current->isRoot());
CodePtr PC = S.Current->getPC();
+ // Empty program.
+ if (!PC)
+ return true;
+
for (;;) {
auto Op = PC.read<Opcode>();
CodePtr OpPC = PC;
Index: clang/lib/AST/Interp/Integral.h
===================================================================
--- clang/lib/AST/Interp/Integral.h
+++ clang/lib/AST/Interp/Integral.h
@@ -203,6 +203,11 @@
return CheckMulUB(A.V, B.V, R->V);
}
+ static bool neg(Integral A, Integral *R) {
+ *R = -A;
+ return false;
+ }
+
private:
template <typename T>
static std::enable_if_t<std::is_signed<T>::value, bool> CheckAddUB(T A, T B,
Index: clang/lib/AST/Interp/Function.h
===================================================================
--- clang/lib/AST/Interp/Function.h
+++ clang/lib/AST/Interp/Function.h
@@ -62,17 +62,21 @@
/// 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.
- CodePtr getCodeBegin() const;
+ CodePtr getCodeBegin() const { return Code.data(); }
/// Returns a pointer to the end of the code.
- CodePtr getCodeEnd() const;
+ CodePtr getCodeEnd() const { return Code.data() + Code.size(); }
/// Returns the original FunctionDecl.
const FunctionDecl *getDecl() const { return F; }
+ /// Returns the name of the function decl this code
+ /// was generated for.
+ const std::string getName() const { return F->getNameInfo().getAsString(); }
+
/// Returns the location.
SourceLocation getLoc() const { return Loc; }
Index: clang/lib/AST/Interp/Function.cpp
===================================================================
--- clang/lib/AST/Interp/Function.cpp
+++ clang/lib/AST/Interp/Function.cpp
@@ -21,10 +21,6 @@
: P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize),
ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {}
-CodePtr Function::getCodeBegin() const { return Code.data(); }
-
-CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); }
-
Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
auto It = Params.find(Offset);
assert(It != Params.end() && "Invalid parameter offset");
Index: clang/lib/AST/Interp/EvalEmitter.cpp
===================================================================
--- clang/lib/AST/Interp/EvalEmitter.cpp
+++ clang/lib/AST/Interp/EvalEmitter.cpp
@@ -102,6 +102,19 @@
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, {}, {});
+ if (Interpret(S, Result)) {
+ // We don't need to handle the return value specially here since the callee
+ // will put it on the stack for us.
+ return true;
+ }
+ return false;
+}
+
bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; }
bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
Index: clang/lib/AST/Interp/Disasm.cpp
===================================================================
--- clang/lib/AST/Interp/Disasm.cpp
+++ clang/lib/AST/Interp/Disasm.cpp
@@ -23,13 +23,13 @@
template <typename T>
inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(Program &P,
- CodePtr OpPC) {
+ CodePtr &OpPC) {
return OpPC.read<T>();
}
template <typename T>
inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(Program &P,
- CodePtr OpPC) {
+ CodePtr &OpPC) {
uint32_t ID = OpPC.read<uint32_t>();
return reinterpret_cast<T>(P.getNativePointer(ID));
}
Index: clang/lib/AST/Interp/Descriptor.h
===================================================================
--- clang/lib/AST/Interp/Descriptor.h
+++ clang/lib/AST/Interp/Descriptor.h
@@ -48,7 +48,7 @@
using InterpSize = unsigned;
/// Describes a memory block created by an allocation site.
-struct Descriptor {
+struct Descriptor final {
private:
/// Original declaration, used to emit the error message.
const DeclTy Source;
@@ -186,7 +186,7 @@
/// A pointer to this is embedded at the end of all primitive arrays.
/// If the map was not yet created and nothing was initialized, the pointer to
/// this structure is 0. If the object was fully initialized, the pointer is -1.
-struct InitMap {
+struct InitMap final {
private:
/// Type packing bits.
using T = uint64_t;
Index: clang/lib/AST/Interp/Context.h
===================================================================
--- clang/lib/AST/Interp/Context.h
+++ clang/lib/AST/Interp/Context.h
@@ -62,10 +62,10 @@
/// Classifies an expression.
llvm::Optional<PrimType> classify(QualType T);
-private:
/// Runs a function.
bool Run(State &Parent, Function *Func, APValue &Result);
+private:
/// Checks a result from the interpreter.
bool Check(State &Parent, llvm::Expected<bool> &&R);
Index: clang/lib/AST/Interp/ByteCodeStmtGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeStmtGen.h
+++ clang/lib/AST/Interp/ByteCodeStmtGen.h
@@ -33,7 +33,7 @@
/// Compilation context for statements.
template <class Emitter>
-class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> {
+class ByteCodeStmtGen final : public ByteCodeExprGen<Emitter> {
using LabelTy = typename Emitter::LabelTy;
using AddrTy = typename Emitter::AddrTy;
using OptLabelTy = llvm::Optional<LabelTy>;
Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -142,6 +142,7 @@
template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
+ llvm::errs() << __PRETTY_FUNCTION__ << "\n";
for (auto *D : DS->decls()) {
// Variable declarator.
if (auto *VD = dyn_cast<VarDecl>(D)) {
@@ -172,7 +173,10 @@
} else {
// RVO - construct the value in the return location.
auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); };
- if (!this->visitInitializer(RE, ReturnLocation))
+ if (!this->visitInitializer(RE, ReturnLocation, [](PrimType T, unsigned) {
+ llvm_unreachable("woops");
+ return false;
+ }))
return false;
this->emitCleanup();
return this->emitRetVoid(RS);
@@ -232,16 +236,15 @@
template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
- auto DT = VD->getType();
-
if (!VD->hasLocalStorage()) {
// No code generation required.
return true;
}
// Integers, pointers, primitives.
- if (Optional<PrimType> T = this->classify(DT)) {
- auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
+ if (Optional<PrimType> T = this->classify(VD->getType())) {
+ auto Offset =
+ this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified());
// Compile the initialiser in its own scope.
{
ExprScope<Emitter> Scope(this);
@@ -249,11 +252,11 @@
return false;
}
// Set the value.
- return this->emitSetLocal(*T, Off, VD);
+ return this->emitSetLocal(*T, Offset, VD);
} else {
// Composite types - allocate storage and initialize it.
- if (auto Off = this->allocateLocal(VD)) {
- return this->visitLocalInitializer(VD->getInit(), *Off);
+ if (auto Offset = this->allocateLocal(VD)) {
+ return this->visitLocalInitializer(VD->getInit(), *Offset);
} else {
return this->bail(VD);
}
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -40,19 +40,12 @@
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
public Emitter {
protected:
- // Emitters for opcodes of various arities.
- using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &);
- using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &);
- using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType,
- const SourceInfo &);
-
- // Aliases for types defined in the emitter.
- using LabelTy = typename Emitter::LabelTy;
- using AddrTy = typename Emitter::AddrTy;
-
- // Reference to a function generating the pointer of an initialized object.s
+ // Reference to a function generating the pointer of an initialized object.
using InitFnRef = std::function<bool()>;
+ // Function initializing a variable with the value on the stack.
+ using InitElemRef = std::function<bool(PrimType, unsigned)>;
+
/// Current compilation context.
Context &Ctx;
/// Program to link to.
@@ -67,8 +60,15 @@
// Expression visitors - result returned on stack.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
+ bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E);
bool VisitParenExpr(const ParenExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
+ bool VisitCallExpr(const CallExpr *E);
+ bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E);
+ bool VisitUnaryOperator(const UnaryOperator *E);
+ bool VisitInitListExpr(const InitListExpr *E);
+ bool VisitDeclRefExpr(const DeclRefExpr *E);
+ bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
protected:
bool visitExpr(const Expr *E) override;
@@ -122,29 +122,35 @@
bool discard(const Expr *E);
/// Evaluates an expression and places result on stack.
bool visit(const Expr *E);
- /// Compiles an initializer for a local.
- bool visitInitializer(const Expr *E, InitFnRef GenPtr);
+ /// Compiles an initializer.
+ bool visitInitializer(const Expr *E, InitFnRef GenPtr, InitElemRef InitElem);
/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
/// Visits an initializer for a local.
bool visitLocalInitializer(const Expr *Init, unsigned I) {
- return visitInitializer(Init, [this, I, Init] {
- return this->emitGetPtrLocal(I, Init);
- });
+ return visitInitializer(
+ Init, [this, I, Init] { return this->emitGetPtrLocal(I, Init); },
+ [this, Init](PrimType T, unsigned ElemIndex) {
+ return this->emitInitElem(T, ElemIndex, Init);
+ });
}
/// Visits an initializer for a global.
bool visitGlobalInitializer(const Expr *Init, unsigned I) {
- return visitInitializer(Init, [this, I, Init] {
- return this->emitGetPtrGlobal(I, Init);
- });
+ return visitInitializer(
+ Init, [this, I, Init] { return this->emitGetPtrGlobal(I, Init); },
+ [this, Init](PrimType T, unsigned ElemIndex) {
+ return this->emitInitElem(T, ElemIndex, Init);
+ });
}
/// Visits a delegated initializer.
bool visitThisInitializer(const Expr *I) {
- return visitInitializer(I, [this, I] { return this->emitThis(I); });
+ return visitInitializer(
+ I, [this, I] { return this->emitThis(I); },
+ [](PrimType T, unsigned EI) { return true; });
}
/// Creates a local primitive value.
@@ -222,9 +228,6 @@
/// Current scope.
VariableScope<Emitter> *VarScope = nullptr;
- /// Current argument index.
- llvm::Optional<uint64_t> ArrayIndex;
-
/// Flag indicating if return value is to be discarded.
bool DiscardResult = false;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -89,20 +89,22 @@
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
auto *SubExpr = CE->getSubExpr();
- switch (CE->getCastKind()) {
+ switch (CE->getCastKind()) {
case CK_LValueToRValue: {
return dereference(
- CE->getSubExpr(), DerefKind::Read,
+ SubExpr, DerefKind::Read,
[](PrimType) {
// Value loaded - nothing to do here.
return true;
},
- [this, CE](PrimType T) {
- // Pointer on stack - dereference it.
- if (!this->emitLoadPop(T, CE))
- return false;
- return DiscardResult ? this->emitPop(T, CE) : true;
+ [this, CE, SubExpr](PrimType T) {
+ if (isa<DeclRefExpr>(SubExpr->IgnoreImpCasts())) {
+ if (!this->emitLoadPop(T, CE))
+ return false;
+ return DiscardResult ? this->emitPop(T, CE) : true;
+ }
+ return true;
});
}
@@ -113,16 +115,20 @@
case CK_NonAtomicToAtomic:
case CK_NoOp:
case CK_UserDefinedConversion:
+ case CK_NullToPointer:
+ case CK_IntegralCast:
return this->Visit(SubExpr);
case CK_ToVoid:
return discard(SubExpr);
- default: {
- // TODO: implement other casts.
- return this->bail(CE);
- }
+ case CK_IntegralToBoolean:
+ return this->visitBool(SubExpr);
+
+ default:
+ llvm_unreachable("cast not implemented");
}
+ return this->bail(CE);
}
template <class Emitter>
@@ -137,6 +143,12 @@
return this->bail(LE);
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXNullPtrLiteralExpr(
+ const CXXNullPtrLiteralExpr *E) {
+ return this->emitNullPtr(E);
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) {
return this->Visit(PE->getSubExpr());
@@ -167,10 +179,29 @@
}
if (Optional<PrimType> T = classify(BO->getType())) {
+ // Handle assignments separately.
+ if (BO->getOpcode() == BO_Assign) {
+ // No type checks here since all this needs to be true right now.
+ if (!visit(LHS))
+ return false;
+ if (!visit(RHS))
+ return false;
+
+ if (!this->emitStore(*T, BO))
+ return false;
+
+ // TODO: This should go away.
+ return this->emitPopPtr(BO);
+ }
+
if (!visit(LHS))
return false;
- if (!visit(RHS))
+
+ if (!visit(RHS)) {
+ // Get rid of LHS on stack.
+ this->emitPop(*LT, BO);
return false;
+ }
auto Discard = [this, T, BO](bool Result) {
if (!Result)
@@ -205,6 +236,32 @@
return this->bail(BO);
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr(
+ const ArraySubscriptExpr *E) {
+ const Expr *Base = E->getBase();
+ const Expr *Index = E->getIdx();
+
+ // Take pointer of LHS, add offset from RHS, dereference result.
+ if (Optional<PrimType> T = classify(E->getType())) {
+ if (!this->Visit(Base))
+ return false;
+
+ if (!this->Visit(Index))
+ return false;
+
+ if (!this->emitAddOffset(*T, E))
+ return false;
+
+ if (!this->emitLoadPop(*T, E))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true);
@@ -461,10 +518,37 @@
}
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::visitInitializer(
- const Expr *Init, InitFnRef InitFn) {
- OptionScope<Emitter> Scope(this, InitFn);
- return this->Visit(Init);
+bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer,
+ InitFnRef GenPtr,
+ InitElemRef InitElem) {
+
+ if (const auto *InitList = cast<InitListExpr>(Initializer)) {
+
+ // Get a pointer to the value being initialized on the stack.
+ if (!GenPtr())
+ return false;
+
+ unsigned ElementIndex = 0;
+ // We assume here that the current stack top is a pointer
+ // to the value being initialized.
+ for (const Expr *Init : InitList->inits()) {
+ if (!this->Visit(Init)) {
+ return false;
+ }
+ Optional<PrimType> InitType = classify(Init);
+ assert(InitType);
+
+ if (!InitElem(*InitType, ElementIndex))
+ return false;
+
+ ++ElementIndex;
+ }
+
+ // Pop the initialized pointer
+ return this->emitPopPtr(Initializer);
+ }
+
+ return this->Visit(Initializer);
}
template <class Emitter>
@@ -564,6 +648,133 @@
return this->bail(VD);
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
+ if (unsigned BuiltinOp = E->getBuiltinCallee()) {
+ switch (BuiltinOp) {
+ default:
+ llvm_unreachable("Builtin function not implemented");
+ }
+ return false;
+ }
+
+ const Decl *Callee = E->getCalleeDecl();
+ if (const auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(Callee)) {
+ Function *Func = P.getFunction(FuncDecl);
+ // TODO: templated functions might need to be instantiated here if they
+ // don't exist yet.
+ assert(Func);
+
+ if (Optional<PrimType> T = classify(E->getType())) {
+ // Put arguments on the stack
+ for (const auto *Arg : E->arguments()) {
+ // TODO: Is this always the correct thing to do?
+ visit(Arg);
+ }
+
+ this->emitCall(*T, Func, E);
+ return true;
+ } 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>::VisitCXXBoolLiteralExpr(
+ const CXXBoolLiteralExpr *E) {
+ if (DiscardResult)
+ return true;
+ return emitConst(E, E->getValue());
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
+ switch (E->getOpcode()) {
+ case UO_PostInc: // x++
+ case UO_PostDec: // x--
+ case UO_PreInc: // --x
+ case UO_PreDec: // ++x
+ return false;
+
+ case UO_LNot: // !x
+ if (!this->Visit(E->getSubExpr()))
+ return false;
+ return this->emitInvBool(E);
+ case UO_Minus: // -x
+ if (!this->Visit(E->getSubExpr()))
+ return false;
+ // TODO: Special-case integer literals.
+ if (Optional<PrimType> T = classify(E->getType()))
+ return this->emitNeg(*T, E);
+ return false;
+ case UO_Plus: // +x
+ return this->Visit(E->getSubExpr()); // noop
+
+ case UO_Deref: // *x
+ return dereference(
+ E->getSubExpr(), DerefKind::Read,
+ [](PrimType) {
+ llvm_unreachable("Dereferencing requires a pointer");
+ return false;
+ },
+ [this, E](PrimType T) {
+ // T: type we get here from classify() is the subexpr
+ // TODO: Is this right?
+ T = *classify(E->getType());
+ if (!this->emitLoadPop(T, E))
+ return false;
+ return DiscardResult ? this->emitPop(T, E) : true;
+ });
+
+ case UO_AddrOf: // &x
+ // Should have a pointer on the stack now...
+ return this->Visit(E->getSubExpr());
+ case UO_Not: // ~x
+ case UO_Real: // __real x
+ case UO_Imag: // __imag x
+ case UO_Extension:
+ case UO_Coawait:
+ llvm_unreachable("Unhandled opcode");
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
+ for (const Expr *Init : E->inits()) {
+ if (!this->visit(Init))
+ return false;
+ }
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
+ if (auto It = Locals.find(E->getDecl()); It != Locals.end()) {
+ const unsigned Offset = It->second.Offset;
+ return this->emitGetPtrLocal(Offset, E);
+ } else if (auto GlobalIndex = P.getGlobal(E->getDecl())) {
+ return this->emitGetPtrGlobal(*GlobalIndex, E);
+ } else if (isa<ParmVarDecl>(E->getDecl())) {
+ // I'm pretty sure we should do something here, BUT
+ // when we're in evaluateAsRValue(), we don't have any parameters,
+ // so we can't actually use this->Params. This happens when
+ // a parameter is used in a return statement.
+ return false;
+ }
+
+ // Silently ignore everything here...
+ return false;
+}
+
template <class Emitter>
void ByteCodeExprGen<Emitter>::emitCleanup() {
for (VariableScope<Emitter> *C = VarScope; C; C = C->getParent())
Index: clang/lib/AST/Interp/Boolean.h
===================================================================
--- clang/lib/AST/Interp/Boolean.h
+++ clang/lib/AST/Interp/Boolean.h
@@ -134,6 +134,16 @@
*R = Boolean(A.V && B.V);
return false;
}
+
+ static bool inv(Boolean A, Boolean *R) {
+ *R = Boolean(!A.V);
+ return false;
+ }
+
+ static bool neg(Boolean A, Boolean *R) {
+ *R = Boolean(-A.V);
+ return false;
+ }
};
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Boolean &B) {
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits