diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang16/lib/AST/Interp/Interp.h | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-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.h | 1309 |
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 |