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<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/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/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/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 <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/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/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 <typename T>
   static std::enable_if_t<std::is_signed<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<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/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 <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/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 <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/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);
+