aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/lib/AST/Interp/Interp.h
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
committervitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
commit6ffe9e53658409f212834330e13564e4952558f6 (patch)
tree85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang16/lib/AST/Interp/Interp.h
parent726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff)
downloadydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/clang16/lib/AST/Interp/Interp.h')
-rw-r--r--contrib/libs/clang16/lib/AST/Interp/Interp.h1309
1 files changed, 1309 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/AST/Interp/Interp.h b/contrib/libs/clang16/lib/AST/Interp/Interp.h
new file mode 100644
index 0000000000..ed3accd98a
--- /dev/null
+++ b/contrib/libs/clang16/lib/AST/Interp/Interp.h
@@ -0,0 +1,1309 @@
+//===--- Interp.h - Interpreter for the constexpr VM ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Definition of the interpreter state and entry point.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INTERP_H
+#define LLVM_CLANG_AST_INTERP_INTERP_H
+
+#include "Boolean.h"
+#include "Function.h"
+#include "InterpFrame.h"
+#include "InterpStack.h"
+#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"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Expr.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/Endian.h"
+#include <limits>
+#include <type_traits>
+
+namespace clang {
+namespace interp {
+
+using APInt = llvm::APInt;
+using APSInt = llvm::APSInt;
+
+/// Convert a value to an APValue.
+template <typename T> bool ReturnValue(const T &V, APValue &R) {
+ R = V.toAPValue();
+ return true;
+}
+
+/// Checks if the variable has externally defined storage.
+bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if the array is offsetable.
+bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a pointer is live and accessible.
+bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK);
+/// Checks if a pointer is null.
+bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ CheckSubobjectKind CSK);
+
+/// Checks if a pointer is in range.
+bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK);
+
+/// Checks if a field from which a pointer is going to be derived is valid.
+bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ CheckSubobjectKind CSK);
+
+/// Checks if a pointer points to const storage.
+bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a pointer points to a mutable field.
+bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a value can be loaded from a block.
+bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a value can be stored in a block.
+bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a method can be invoked on an object.
+bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a value can be initialized.
+bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a method can be called.
+bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
+
+/// Checks the 'this' pointer.
+bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
+
+/// Checks if a method is pure virtual.
+bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
+
+/// Checks that all fields are initialized after a constructor call.
+bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This);
+
+/// Checks if the shift operation is legal.
+template <typename RT>
+bool CheckShift(InterpState &S, CodePtr OpPC, const RT &RHS, unsigned Bits) {
+ if (RHS.isNegative()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
+ return false;
+ }
+
+ // C++11 [expr.shift]p1: Shift width must be less than the bit width of
+ // the shifted type.
+ if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ const APSInt Val = RHS.toAPSInt();
+ QualType Ty = E->getType();
+ S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
+ return false;
+ }
+ return true;
+}
+
+/// Checks if Div/Rem operation on LHS and RHS is valid.
+template <typename T>
+bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
+ if (RHS.isZero()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_expr_divide_by_zero);
+ return false;
+ }
+
+ if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) {
+ APSInt LHSInt = LHS.toAPSInt();
+ SmallString<32> Trunc;
+ (-LHSInt.extend(LHSInt.getBitWidth() + 1)).toString(Trunc, 10);
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_overflow) << Trunc << E->getType();
+ return false;
+ }
+ return true;
+}
+
+/// Interpreter entry point.
+bool Interpret(InterpState &S, APValue &Result);
+
+//===----------------------------------------------------------------------===//
+// Add, Sub, Mul
+//===----------------------------------------------------------------------===//
+
+template <typename T, bool (*OpFW)(T, T, unsigned, T *),
+ template <typename U> class OpAP>
+bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
+ const T &RHS) {
+ // Fast path - add the numbers with fixed width.
+ T Result;
+ if (!OpFW(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+
+ // If for some reason evaluation continues, use the truncated results.
+ S.Stk.push<T>(Result);
+
+ // Slow path - compute the result using another bit of precision.
+ APSInt Value = OpAP<APSInt>()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits));
+
+ // Report undefined behaviour, stopping if required.
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+ if (S.checkingForUndefinedBehavior()) {
+ SmallString<32> Trunc;
+ Value.trunc(Result.bitWidth()).toString(Trunc, 10);
+ auto Loc = E->getExprLoc();
+ S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
+ return true;
+ } else {
+ S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
+ return S.noteUndefinedBehavior();
+ }
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Add(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() + 1;
+ return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Sub(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() + 1;
+ return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Mul(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() * 2;
+ return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS & RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitAnd(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitAnd(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS | RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitOr(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitOr(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS ^ RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitXor(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitXor(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS).
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Rem(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ const unsigned Bits = RHS.bitWidth() * 2;
+ T Result;
+ if (!T::rem(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS / RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Div(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ const unsigned Bits = RHS.bitWidth() * 2;
+ T Result;
+ if (!T::div(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// 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;
+}
+
+enum class PushVal : bool {
+ No,
+ Yes,
+};
+enum class IncDecOp {
+ Inc,
+ Dec,
+};
+
+template <typename T, IncDecOp Op, PushVal DoPush>
+bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ T Value = Ptr.deref<T>();
+ T Result;
+
+ if constexpr (DoPush == PushVal::Yes)
+ S.Stk.push<T>(Result);
+
+ if constexpr (Op == IncDecOp::Inc) {
+ if (!T::increment(Value, &Result)) {
+ Ptr.deref<T>() = Result;
+ return true;
+ }
+ } else {
+ if (!T::decrement(Value, &Result)) {
+ Ptr.deref<T>() = Result;
+ return true;
+ }
+ }
+
+ // Something went wrong with the previous operation. Compute the
+ // result with another bit of precision.
+ unsigned Bits = Value.bitWidth() + 1;
+ APSInt APResult;
+ if constexpr (Op == IncDecOp::Inc)
+ APResult = ++Value.toAPSInt(Bits);
+ else
+ APResult = --Value.toAPSInt(Bits);
+
+ // Report undefined behaviour, stopping if required.
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+ if (S.checkingForUndefinedBehavior()) {
+ SmallString<32> Trunc;
+ APResult.trunc(Result.bitWidth()).toString(Trunc, 10);
+ auto Loc = E->getExprLoc();
+ S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
+ return true;
+ }
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type;
+ return S.noteUndefinedBehavior();
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+/// 4) Pushes the original (pre-inc) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inc(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool IncPop(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+/// 4) Pushes the original (pre-dec) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dec(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool DecPop(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
+}
+
+/// 1) Pops the value from the stack.
+/// 2) Pushes the bitwise complemented value on the stack (~V).
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Comp(InterpState &S, CodePtr OpPC) {
+ const T &Val = S.Stk.pop<T>();
+ T Result;
+ if (!T::comp(Val, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// EQ, NE, GT, GE, LT, LE
+//===----------------------------------------------------------------------===//
+
+using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
+
+template <typename T>
+bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
+ return true;
+}
+
+template <typename T>
+bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ return CmpHelper<T>(S, OpPC, Fn);
+}
+
+template <>
+inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+
+ if (!Pointer::hasSameBase(LHS, RHS)) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ } else {
+ unsigned VL = LHS.getByteOffset();
+ unsigned VR = RHS.getByteOffset();
+ S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
+ return true;
+ }
+}
+
+template <>
+inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+
+ if (LHS.isZero() && RHS.isZero()) {
+ S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
+ return true;
+ }
+
+ if (!Pointer::hasSameBase(LHS, RHS)) {
+ S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
+ return true;
+ } else {
+ unsigned VL = LHS.getByteOffset();
+ unsigned VR = RHS.getByteOffset();
+
+ // In our Pointer class, a pointer to an array and a pointer to the first
+ // element in the same array are NOT equal. They have the same Base value,
+ // but a different Offset. This is a pretty rare case, so we fix this here
+ // by comparing pointers to the first elements.
+ if (LHS.inArray() && LHS.isRoot())
+ VL = LHS.atIndex(0).getByteOffset();
+ if (RHS.inArray() && RHS.isRoot())
+ VR = RHS.atIndex(0).getByteOffset();
+
+ S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
+ return true;
+ }
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool EQ(InterpState &S, CodePtr OpPC) {
+ return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Equal;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool NE(InterpState &S, CodePtr OpPC) {
+ return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R != ComparisonCategoryResult::Equal;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool LT(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Less;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool LE(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Less ||
+ R == ComparisonCategoryResult::Equal;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GT(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Greater;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GE(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Greater ||
+ R == ComparisonCategoryResult::Equal;
+ });
+}
+
+//===----------------------------------------------------------------------===//
+// InRange
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InRange(InterpState &S, CodePtr OpPC) {
+ const T RHS = S.Stk.pop<T>();
+ const T LHS = S.Stk.pop<T>();
+ const T Value = S.Stk.pop<T>();
+
+ S.Stk.push<bool>(LHS <= Value && Value <= RHS);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Dup, Pop, Test
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dup(InterpState &S, CodePtr OpPC) {
+ S.Stk.push<T>(S.Stk.peek<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Pop(InterpState &S, CodePtr OpPC) {
+ S.Stk.pop<T>();
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Const
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
+ S.Stk.push<T>(Arg);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Get/Set Local/Param/Global/This
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const Pointer &Ptr = S.Current->getLocalPointer(I);
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Current->setLocal<T>(I, S.Stk.pop<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression()) {
+ return false;
+ }
+ S.Stk.push<T>(S.Current->getParam<T>(I));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Current->setParam<T>(I, S.Stk.pop<T>());
+ return true;
+}
+
+/// 1) Peeks a pointer on the stack
+/// 2) Pushes the value of the pointer's field on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const Pointer &Obj = S.Stk.peek<Pointer>();
+ if (!CheckNull(S, OpPC, Obj, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Obj, CSK_Field))
+ return false;
+ const Pointer &Field = Obj.atField(I);
+ if (!CheckLoad(S, OpPC, Field))
+ return false;
+ S.Stk.push<T>(Field.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Obj = S.Stk.peek<Pointer>();
+ if (!CheckNull(S, OpPC, Obj, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Obj, CSK_Field))
+ return false;
+ const Pointer &Field = Obj.atField(I);
+ if (!CheckStore(S, OpPC, Field))
+ return false;
+ Field.deref<T>() = Value;
+ return true;
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Pushes the value of the pointer's field on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const Pointer &Obj = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Obj, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Obj, CSK_Field))
+ return false;
+ const Pointer &Field = Obj.atField(I);
+ if (!CheckLoad(S, OpPC, Field))
+ return false;
+ S.Stk.push<T>(Field.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ if (!CheckLoad(S, OpPC, Field))
+ return false;
+ S.Stk.push<T>(Field.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ if (!CheckStore(S, OpPC, Field))
+ return false;
+ Field.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ auto *B = S.P.getGlobal(I);
+ if (B->isExtern())
+ return false;
+ S.Stk.push<T>(B->deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ // TODO: emit warning.
+ return false;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ Field.deref<T>() = S.Stk.pop<T>();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(F->Offset);
+ const auto &Value = S.Stk.pop<T>();
+ Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ Field.deref<T>() = S.Stk.pop<T>();
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer from the stack
+/// 3) Pushes the value to field I of the pointer on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Field = S.Stk.peek<Pointer>().atField(I);
+ Field.deref<T>() = Value;
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Field = S.Stk.pop<Pointer>().atField(F->Offset);
+ Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ const Pointer &Field = Ptr.atField(I);
+ Field.deref<T>() = Value;
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// GetPtr Local/Param/Global/Field/This
+//===----------------------------------------------------------------------===//
+
+inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Stk.push<Pointer>(S.Current->getLocalPointer(I));
+ return true;
+}
+
+inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression()) {
+ return false;
+ }
+ S.Stk.push<Pointer>(S.Current->getParamPointer(I));
+ return true;
+}
+
+inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Stk.push<Pointer>(S.P.getPtrGlobal(I));
+ return true;
+}
+
+/// 1) Pops a Pointer from the stack
+/// 2) Pushes Pointer.atField(Off) on the stack
+inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Field))
+ return false;
+ if (!CheckExtern(S, OpPC, Ptr))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, CSK_Field))
+ return false;
+ S.Stk.push<Pointer>(Ptr.atField(Off));
+ return true;
+}
+
+inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ S.Stk.push<Pointer>(This.atField(Off));
+ return true;
+}
+
+inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, CSK_Field))
+ return false;
+ Pointer Field = Ptr.atField(Off);
+ Ptr.deactivate();
+ Field.activate();
+ S.Stk.push<Pointer>(std::move(Field));
+ return true;
+}
+
+inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ Pointer Field = This.atField(Off);
+ This.deactivate();
+ Field.activate();
+ S.Stk.push<Pointer>(std::move(Field));
+ return true;
+}
+
+inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Base))
+ return false;
+ S.Stk.push<Pointer>(Ptr.atField(Off));
+ return true;
+}
+
+inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ S.Stk.push<Pointer>(This.atField(Off));
+ return true;
+}
+
+inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
+ const Pointer &Ptr) {
+ Pointer Base = Ptr;
+ while (Base.isBaseClass())
+ Base = Base.getBase();
+
+ auto *Field = Base.getRecord()->getVirtualBase(Decl);
+ S.Stk.push<Pointer>(Base.atField(Field->Offset));
+ return true;
+}
+
+inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Base))
+ return false;
+ return VirtBaseHelper(S, OpPC, D, Ptr);
+}
+
+inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC,
+ const RecordDecl *D) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ return VirtBaseHelper(S, OpPC, D, S.Current->getThis());
+}
+
+//===----------------------------------------------------------------------===//
+// Load, Store, Init
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Load(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool LoadPop(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Store(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StorePop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreBitField(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
+ if (auto *FD = Ptr.getField()) {
+ Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
+ } else {
+ Ptr.deref<T>() = Value;
+ }
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
+ if (auto *FD = Ptr.getField()) {
+ Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
+ } else {
+ Ptr.deref<T>() = Value;
+ }
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitPop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckInit(S, OpPC, Ptr))
+ return false;
+ Ptr.initialize();
+ new (&Ptr.deref<T>()) T(Value);
+ return true;
+}
+
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer and gets its index \Idx
+/// 3) Sets the value on the pointer, leaving the pointer on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>().atIndex(Idx);
+ if (!CheckInit(S, OpPC, Ptr))
+ return false;
+ Ptr.initialize();
+ new (&Ptr.deref<T>()) T(Value);
+ return true;
+}
+
+/// The same as InitElem, but pops the pointer as well.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>().atIndex(Idx);
+ if (!CheckInit(S, OpPC, Ptr))
+ return false;
+ Ptr.initialize();
+ new (&Ptr.deref<T>()) T(Value);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// AddOffset, SubOffset
+//===----------------------------------------------------------------------===//
+
+template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) {
+ // Fetch the pointer and the offset.
+ const T &Offset = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
+ return false;
+
+ // A zero offset does not change the pointer.
+ if (Offset.isZero()) {
+ S.Stk.push<Pointer>(Ptr);
+ return true;
+ }
+
+ if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
+ return false;
+
+ // Arrays of unknown bounds cannot have pointers into them.
+ if (!CheckArray(S, OpPC, Ptr))
+ return false;
+
+ // Get a version of the index comparable to the type.
+ T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
+ // Compute the largest index into the array.
+ unsigned MaxIndex = Ptr.getNumElems();
+
+ // Helper to report an invalid offset, computed as APSInt.
+ auto InvalidOffset = [&]() {
+ const unsigned Bits = Offset.bitWidth();
+ APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
+ APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
+ APSInt NewIndex = Add ? (APIndex + APOffset) : (APIndex - APOffset);
+ S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+ << NewIndex
+ << /*array*/ static_cast<int>(!Ptr.inArray())
+ << static_cast<unsigned>(MaxIndex);
+ return false;
+ };
+
+ unsigned MaxOffset = MaxIndex - Ptr.getIndex();
+ if constexpr (Add) {
+ // If the new offset would be negative, bail out.
+ if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
+ return InvalidOffset();
+
+ // If the new offset would be out of bounds, bail out.
+ if (Offset.isPositive() && Offset > MaxOffset)
+ return InvalidOffset();
+ } else {
+ // If the new offset would be negative, bail out.
+ if (Offset.isPositive() && Index < Offset)
+ return InvalidOffset();
+
+ // If the new offset would be out of bounds, bail out.
+ if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
+ return InvalidOffset();
+ }
+
+ // Offset is valid - compute it on unsigned.
+ int64_t WideIndex = static_cast<int64_t>(Index);
+ int64_t WideOffset = static_cast<int64_t>(Offset);
+ int64_t Result;
+ if constexpr (Add)
+ Result = WideIndex + WideOffset;
+ else
+ Result = WideIndex - WideOffset;
+
+ S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result)));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool AddOffset(InterpState &S, CodePtr OpPC) {
+ return OffsetHelper<T, true>(S, OpPC);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SubOffset(InterpState &S, CodePtr OpPC) {
+ return OffsetHelper<T, false>(S, OpPC);
+}
+
+/// 1) Pops a Pointer from the stack.
+/// 2) Pops another Pointer from the stack.
+/// 3) Pushes the different of the indices of the two pointers on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool SubPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+
+ if (!Pointer::hasSameArray(LHS, RHS)) {
+ // TODO: Diagnose.
+ return false;
+ }
+
+ T A = T::from(LHS.getIndex());
+ T B = T::from(RHS.getIndex());
+ return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B);
+}
+
+//===----------------------------------------------------------------------===//
+// Destroy
+//===----------------------------------------------------------------------===//
+
+inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Current->destroy(I);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Cast, CastFP
+//===----------------------------------------------------------------------===//
+
+template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
+ using T = typename PrimConv<TIn>::T;
+ using U = typename PrimConv<TOut>::T;
+ S.Stk.push<U>(U::from(S.Stk.pop<T>()));
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Zero, Nullptr
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Zero(InterpState &S, CodePtr OpPC) {
+ S.Stk.push<T>(T::zero());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool Null(InterpState &S, CodePtr OpPC) {
+ S.Stk.push<T>();
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// This, ImplicitThis
+//===----------------------------------------------------------------------===//
+
+inline bool This(InterpState &S, CodePtr OpPC) {
+ // Cannot read 'this' in this mode.
+ if (S.checkingPotentialConstantExpression()) {
+ return false;
+ }
+
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+
+ S.Stk.push<Pointer>(This);
+ return true;
+}
+
+inline bool RVOPtr(InterpState &S, CodePtr OpPC) {
+ assert(S.Current->getFunction()->hasRVO());
+ S.Stk.push<Pointer>(S.Current->getRVOPtr());
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Shr, Shl
+//===----------------------------------------------------------------------===//
+
+template <PrimType NameL, PrimType NameR>
+inline bool Shr(InterpState &S, CodePtr OpPC) {
+ using LT = typename PrimConv<NameL>::T;
+ using RT = typename PrimConv<NameR>::T;
+ const auto &RHS = S.Stk.pop<RT>();
+ const auto &LHS = S.Stk.pop<LT>();
+ const unsigned Bits = LHS.bitWidth();
+
+ if (!CheckShift<RT>(S, OpPC, RHS, Bits))
+ return false;
+
+ unsigned URHS = static_cast<unsigned>(RHS);
+ S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth()));
+ return true;
+}
+
+template <PrimType NameL, PrimType NameR>
+inline bool Shl(InterpState &S, CodePtr OpPC) {
+ using LT = typename PrimConv<NameL>::T;
+ using RT = typename PrimConv<NameR>::T;
+ const auto &RHS = S.Stk.pop<RT>();
+ const auto &LHS = S.Stk.pop<LT>();
+ const unsigned Bits = LHS.bitWidth();
+
+ if (!CheckShift<RT>(S, OpPC, RHS, Bits))
+ return false;
+
+ unsigned URHS = static_cast<unsigned>(RHS);
+ S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth()));
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// NoRet
+//===----------------------------------------------------------------------===//
+
+inline bool NoRet(InterpState &S, CodePtr OpPC) {
+ SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
+ S.FFDiag(EndLoc, diag::note_constexpr_no_return);
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// NarrowPtr, ExpandPtr
+//===----------------------------------------------------------------------===//
+
+inline bool NarrowPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ S.Stk.push<Pointer>(Ptr.narrow());
+ return true;
+}
+
+inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ S.Stk.push<Pointer>(Ptr.expand());
+ return true;
+}
+
+inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) {
+ auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC);
+ Pointer ThisPtr;
+ if (Func->hasThisPointer()) {
+ ThisPtr = NewFrame->getThis();
+ if (!CheckInvoke(S, PC, ThisPtr)) {
+ return false;
+ }
+ }
+
+ if (!CheckCallable(S, PC, Func))
+ return false;
+
+ InterpFrame *FrameBefore = S.Current;
+ S.Current = NewFrame.get();
+
+ APValue CallResult;
+ // Note that we cannot assert(CallResult.hasValue()) here since
+ // Ret() above only sets the APValue if the curent frame doesn't
+ // have a caller set.
+ if (Interpret(S, CallResult)) {
+ NewFrame.release(); // Frame was delete'd already.
+ assert(S.Current == FrameBefore);
+
+ // For constructors, check that all fields have been initialized.
+ if (Func->isConstructor() && !CheckCtorCall(S, PC, ThisPtr))
+ return false;
+
+ return true;
+ }
+
+ // Interpreting the function failed somehow. Reset to
+ // previous state.
+ S.Current = FrameBefore;
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Read opcode arguments
+//===----------------------------------------------------------------------===//
+
+template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) {
+ if constexpr (std::is_pointer<T>::value) {
+ uint32_t ID = OpPC.read<uint32_t>();
+ return reinterpret_cast<T>(S.P.getNativePointer(ID));
+ } else {
+ return OpPC.read<T>();
+ }
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif