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) { 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, 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; + // Function initializing a variable with the value on the stack. + using InitElemRef = std::function; + /// 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 *VarScope = nullptr; - /// Current argument index. - llvm::Optional 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 bool ByteCodeExprGen::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(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 @@ -137,6 +143,12 @@ return this->bail(LE); } +template +bool ByteCodeExprGen::VisitCXXNullPtrLiteralExpr( + const CXXNullPtrLiteralExpr *E) { + return this->emitNullPtr(E); +} + template bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *PE) { return this->Visit(PE->getSubExpr()); @@ -167,10 +179,29 @@ } if (Optional 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 +bool ByteCodeExprGen::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 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 bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); @@ -461,10 +518,37 @@ } template -bool ByteCodeExprGen::visitInitializer( - const Expr *Init, InitFnRef InitFn) { - OptionScope Scope(this, InitFn); - return this->Visit(Init); +bool ByteCodeExprGen::visitInitializer(const Expr *Initializer, + InitFnRef GenPtr, + InitElemRef InitElem) { + + if (const auto *InitList = cast(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 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 @@ -564,6 +648,133 @@ return this->bail(VD); } +template +bool ByteCodeExprGen::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(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 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 +bool ByteCodeExprGen::VisitCXXBoolLiteralExpr( + const CXXBoolLiteralExpr *E) { + if (DiscardResult) + return true; + return emitConst(E, E->getValue()); +} + +template +bool ByteCodeExprGen::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 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 +bool ByteCodeExprGen::VisitInitListExpr(const InitListExpr *E) { + for (const Expr *Init : E->inits()) { + if (!this->visit(Init)) + return false; + } + + return true; +} + +template +bool ByteCodeExprGen::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(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 void ByteCodeExprGen::emitCleanup() { for (VariableScope *C = VarScope; C; C = C->getParent()) 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 ByteCodeStmtGen : public ByteCodeExprGen { +class ByteCodeStmtGen final : public ByteCodeExprGen { using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; using OptLabelTy = llvm::Optional; Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -142,6 +142,7 @@ template bool ByteCodeStmtGen::visitDeclStmt(const DeclStmt *DS) { + llvm::errs() << __PRETTY_FUNCTION__ << "\n"; for (auto *D : DS->decls()) { // Variable declarator. if (auto *VD = dyn_cast(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 bool ByteCodeStmtGen::visitVarDecl(const VarDecl *VD) { - auto DT = VD->getType(); - if (!VD->hasLocalStorage()) { // No code generation required. return true; } // Integers, pointers, primitives. - if (Optional T = this->classify(DT)) { - auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); + if (Optional T = this->classify(VD->getType())) { + auto Offset = + this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified()); // Compile the initialiser in its own scope. { ExprScope 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/Context.h =================================================================== --- clang/lib/AST/Interp/Context.h +++ clang/lib/AST/Interp/Context.h @@ -62,10 +62,10 @@ /// Classifies an expression. llvm::Optional 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 &&R); 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/Disasm.cpp =================================================================== --- clang/lib/AST/Interp/Disasm.cpp +++ clang/lib/AST/Interp/Disasm.cpp @@ -23,13 +23,13 @@ template inline std::enable_if_t::value, T> ReadArg(Program &P, - CodePtr OpPC) { + CodePtr &OpPC) { return OpPC.read(); } template inline std::enable_if_t::value, T> ReadArg(Program &P, - CodePtr OpPC) { + CodePtr &OpPC) { uint32_t ID = OpPC.read(); return reinterpret_cast(P.getNativePointer(ID)); } 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(S.Stk.pop(), Result); } +template +bool EvalEmitter::emitCall(const Function *Func, const SourceInfo &Info) { + + S.Current = + new InterpFrame(S, const_cast(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/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/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 static std::enable_if_t::value, bool> CheckAddUB(T A, T B, 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(S, OpPC, Bits, LHS, RHS); } +//===----------------------------------------------------------------------===// +// Inv +//===----------------------------------------------------------------------===// + +template ::T> +bool Inv(InterpState &S, CodePtr OpPC) { + using BoolT = PrimConv::T; + const T &Val = S.Stk.pop(); + const unsigned Bits = Val.bitWidth(); + Boolean R; + Boolean::inv(BoolT::from(Val, Bits), &R); + + S.Stk.push(R); + return true; +} + +//===----------------------------------------------------------------------===// +// Neg +//===----------------------------------------------------------------------===// + +template ::T> +bool Neg(InterpState &S, CodePtr OpPC) { + const T &Val = S.Stk.pop(); + T Result; + T::neg(Val, &Result); + + S.Stk.push(Result); + return true; +} + //===----------------------------------------------------------------------===// // EQ, NE, GT, GE, LT, LE //===----------------------------------------------------------------------===// @@ -412,7 +441,7 @@ template ::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 ::T> bool Store(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop(); @@ -956,13 +988,13 @@ template inline std::enable_if_t::value, T> ReadArg(InterpState &S, - CodePtr OpPC) { + CodePtr &OpPC) { return OpPC.read(); } template inline std::enable_if_t::value, T> ReadArg(InterpState &S, - CodePtr OpPC) { + CodePtr &OpPC) { uint32_t ID = OpPC.read(); return reinterpret_cast(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 ::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(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(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(); CodePtr OpPC = PC; 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/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/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 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(); + 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/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/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/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/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/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 std::enable_if_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/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 +//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); +