| //===------- Char.h - Wrapper for numeric types for the 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_CLANG_AST_INTERP_CHAR_H | |
| #define LLVM_CLANG_AST_INTERP_CHAR_H | |
| #include "Integral.h" | |
| #include <limits> | |
| namespace clang { | |
| namespace interp { | |
| template <unsigned N, bool Signed> class Integral; | |
| template <bool Signed> struct CharRepr; | |
| template <> struct CharRepr<false> { | |
| using Type = uint8_t; | |
| }; | |
| template <> struct CharRepr<true> { | |
| using Type = int8_t; | |
| }; | |
| template <bool Signed> class Char final { | |
| private: | |
| template <bool OtherSigned> friend class Char; | |
| using ReprT = typename CharRepr<Signed>::Type; | |
| ReprT V = 0; | |
| static_assert(std::is_trivially_copyable_v<ReprT>); | |
| public: | |
| using AsUnsigned = Char<false>; | |
| constexpr Char() = default; | |
| constexpr Char(ReprT V) : V(V) {} | |
| // constexpr Char(const Char &C) : V(C.V) {} | |
| explicit Char(const APSInt &V) | |
| : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {} | |
| template <typename T> static Char from(T t) { | |
| return Char(static_cast<ReprT>(t)); | |
| } | |
| template <typename T> static Char from(T t, unsigned BitWidth) { | |
| return Char(static_cast<ReprT>(t)); | |
| } | |
| static bool isSigned() { return Signed; } | |
| static unsigned bitWidth() { return 8; } | |
| static bool isNumber() { return true; } | |
| static Char zero(unsigned BitWidth = 8) { return Char(0); } | |
| constexpr bool isMin() const { | |
| return V == std::numeric_limits<ReprT>::min(); | |
| } | |
| constexpr bool isNegative() const { return Signed && V < 0; } | |
| constexpr bool isPositive() const { return !isNegative(); } | |
| constexpr bool isZero() const { return V == 0; } | |
| constexpr bool isMinusOne() const { return Signed && V == -1; } | |
| template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>> | |
| explicit operator Ty() const { | |
| return V; | |
| } | |
| bool operator<(Char RHS) const { return V < RHS.V; } | |
| bool operator>(Char RHS) const { return V > RHS.V; } | |
| bool operator<=(Char RHS) const { return V <= RHS.V; } | |
| bool operator>=(Char RHS) const { return V >= RHS.V; } | |
| bool operator==(Char RHS) const { return V == RHS.V; } | |
| bool operator!=(Char RHS) const { return V != RHS.V; } | |
| bool operator>=(unsigned RHS) const { | |
| return static_cast<unsigned>(V) >= RHS; | |
| } | |
| bool operator>(unsigned RHS) const { | |
| return V >= 0 && static_cast<unsigned>(V) > RHS; | |
| } | |
| Char operator-() const { return Char(-V); } | |
| Char operator-(Char Other) const { return Char(V - Other.V); } | |
| ComparisonCategoryResult compare(Char RHS) const { return Compare(V, RHS.V); } | |
| void bitcastToMemory(std::byte *Dest) const { | |
| std::memcpy(Dest, &V, sizeof(V)); | |
| } | |
| static Char bitcastFromMemory(const std::byte *Src, unsigned BitWidth) { | |
| assert(BitWidth == 8); | |
| ReprT V; | |
| std::memcpy(&V, Src, sizeof(ReprT)); | |
| return Char(V); | |
| } | |
| APSInt toAPSInt() const { | |
| return APSInt(APInt(8, static_cast<uint64_t>(V), Signed), !Signed); | |
| } | |
| APSInt toAPSInt(unsigned BitWidth) const { | |
| return APSInt(toAPInt(BitWidth), !Signed); | |
| } | |
| APInt toAPInt(unsigned BitWidth) const { | |
| if constexpr (Signed) | |
| return APInt(8, static_cast<uint64_t>(V), Signed).sextOrTrunc(BitWidth); | |
| else | |
| return APInt(8, static_cast<uint64_t>(V), Signed).zextOrTrunc(BitWidth); | |
| } | |
| APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); } | |
| std::string toDiagnosticString(const ASTContext &Ctx) const { | |
| std::string NameStr; | |
| llvm::raw_string_ostream OS(NameStr); | |
| OS << V; | |
| return NameStr; | |
| } | |
| Char<false> toUnsigned() const { return Char<false>(V); } | |
| Char truncate(unsigned TruncBits) const { | |
| assert(TruncBits >= 1); | |
| if (TruncBits >= 8) | |
| return *this; | |
| const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1; | |
| const ReprT SignBit = ReprT(1) << (TruncBits - 1); | |
| const ReprT ExtMask = ~BitMask; | |
| return Char((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0)); | |
| } | |
| unsigned countLeadingZeros() const { | |
| if constexpr (!Signed) | |
| return llvm::countl_zero<ReprT>(V); | |
| if (isPositive()) | |
| return llvm::countl_zero<typename AsUnsigned::ReprT>( | |
| static_cast<typename AsUnsigned::ReprT>(V)); | |
| llvm_unreachable("Don't call countLeadingZeros() on negative values."); | |
| } | |
| static bool increment(Char A, Char *R) { | |
| return add(A, Char(ReprT(1)), A.bitWidth(), R); | |
| } | |
| static bool decrement(Char A, Char *R) { | |
| return sub(A, Char(ReprT(1)), A.bitWidth(), R); | |
| } | |
| static bool add(Char A, Char B, unsigned OpBits, Char *R) { | |
| return CheckAddUB(A.V, B.V, R->V); | |
| } | |
| static bool sub(Char A, Char B, unsigned OpBits, Char *R) { | |
| return CheckSubUB(A.V, B.V, R->V); | |
| } | |
| static bool mul(Char A, Char B, unsigned OpBits, Char *R) { | |
| return CheckMulUB(A.V, B.V, R->V); | |
| } | |
| static bool rem(Char A, Char B, unsigned OpBits, Char *R) { | |
| *R = Char(A.V % B.V); | |
| return false; | |
| } | |
| static bool div(Char A, Char B, unsigned OpBits, Char *R) { | |
| *R = Char(A.V / B.V); | |
| return false; | |
| } | |
| static bool bitAnd(Char A, Char B, unsigned OpBits, Char *R) { | |
| *R = Char(A.V & B.V); | |
| return false; | |
| } | |
| static bool bitOr(Char A, Char B, unsigned OpBits, Char *R) { | |
| *R = Char(A.V | B.V); | |
| return false; | |
| } | |
| static bool bitXor(Char A, Char B, unsigned OpBits, Char *R) { | |
| *R = Char(A.V ^ B.V); | |
| return false; | |
| } | |
| static bool neg(Char A, Char *R) { | |
| if (Signed && A.isMin()) | |
| return true; | |
| *R = Char(-A.V); | |
| return false; | |
| } | |
| static bool comp(Char A, Char *R) { | |
| *R = Char(~A.V); | |
| return false; | |
| } | |
| template <bool RHSSign> | |
| static void shiftLeft(const Char A, const Char<RHSSign> B, unsigned OpBits, | |
| Char *R) { | |
| *R = Char(A.V << B.V); | |
| } | |
| template <bool RHSSign> | |
| static void shiftRight(const Char A, const Char<RHSSign> B, unsigned OpBits, | |
| Char *R) { | |
| *R = Char(A.V >> B.V); | |
| } | |
| void print(llvm::raw_ostream &OS) const { OS << V; } | |
| }; | |
| static_assert(sizeof(Char<true>) == 1); | |
| static_assert(sizeof(Char<false>) == 1); | |
| template <bool Signed> | |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Char<Signed> I) { | |
| I.print(OS); | |
| return OS; | |
| } | |
| } // namespace interp | |
| } // namespace clang | |
| #endif |