| //===--- Floating.h - Types 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Defines the VM types and helpers operating on types. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_AST_INTERP_FLOATING_H |
| #define LLVM_CLANG_AST_INTERP_FLOATING_H |
| |
| #include "Primitives.h" |
| #include "clang/AST/APValue.h" |
| #include "llvm/ADT/APFloat.h" |
| |
| // XXX This is just a debugging help. Setting this to 1 will heap-allocate ALL |
| // floating values. |
| #define ALLOCATE_ALL 0 |
| |
| namespace clang { |
| namespace interp { |
| |
| using APFloat = llvm::APFloat; |
| using APSInt = llvm::APSInt; |
| using APInt = llvm::APInt; |
| |
| /// If a Floating is constructed from Memory, it DOES NOT OWN THAT MEMORY. |
| /// It will NOT copy the memory (unless, of course, copy() is called) and it |
| /// won't alllocate anything. The allocation should happen via InterpState or |
| /// Program. |
| class Floating final { |
| private: |
| union { |
| uint64_t Val = 0; |
| uint64_t *Memory; |
| }; |
| llvm::APFloatBase::Semantics Semantics; |
| |
| APFloat getValue() const { |
| unsigned BitWidth = bitWidth(); |
| if (singleWord()) |
| return APFloat(getSemantics(), APInt(BitWidth, Val)); |
| unsigned NumWords = numWords(); |
| return APFloat(getSemantics(), APInt(BitWidth, NumWords, Memory)); |
| } |
| |
| public: |
| Floating() = default; |
| Floating(llvm::APFloatBase::Semantics Semantics) |
| : Val(0), Semantics(Semantics) {} |
| Floating(const APFloat &F) { |
| |
| Semantics = llvm::APFloatBase::SemanticsToEnum(F.getSemantics()); |
| this->copy(F); |
| } |
| Floating(uint64_t *Memory, llvm::APFloatBase::Semantics Semantics) |
| : Memory(Memory), Semantics(Semantics) {} |
| |
| APFloat getAPFloat() const { return getValue(); } |
| |
| bool operator<(Floating RHS) const { return getValue() < RHS.getValue(); } |
| bool operator>(Floating RHS) const { return getValue() > RHS.getValue(); } |
| bool operator<=(Floating RHS) const { return getValue() <= RHS.getValue(); } |
| bool operator>=(Floating RHS) const { return getValue() >= RHS.getValue(); } |
| |
| APFloat::opStatus convertToInteger(APSInt &Result) const { |
| bool IsExact; |
| return getValue().convertToInteger(Result, llvm::APFloat::rmTowardZero, |
| &IsExact); |
| } |
| |
| void toSemantics(const llvm::fltSemantics *Sem, llvm::RoundingMode RM, |
| Floating *Result) const { |
| APFloat Copy = getValue(); |
| bool LosesInfo; |
| Copy.convert(*Sem, RM, &LosesInfo); |
| (void)LosesInfo; |
| Result->copy(Copy); |
| } |
| |
| APSInt toAPSInt(unsigned NumBits = 0) const { |
| return APSInt(getValue().bitcastToAPInt()); |
| } |
| APValue toAPValue(const ASTContext &) const { return APValue(getValue()); } |
| void print(llvm::raw_ostream &OS) const { |
| // Can't use APFloat::print() since it appends a newline. |
| SmallVector<char, 16> Buffer; |
| getValue().toString(Buffer); |
| OS << Buffer; |
| } |
| std::string toDiagnosticString(const ASTContext &Ctx) const { |
| std::string NameStr; |
| llvm::raw_string_ostream OS(NameStr); |
| print(OS); |
| return NameStr; |
| } |
| |
| unsigned bitWidth() const { |
| return llvm::APFloatBase::semanticsSizeInBits(getSemantics()); |
| } |
| unsigned numWords() const { return llvm::APInt::getNumWords(bitWidth()); } |
| bool singleWord() const { |
| #if ALLOCATE_ALL |
| return false; |
| #endif |
| return numWords() == 1; |
| } |
| static bool singleWord(const llvm::fltSemantics &Sem) { |
| #if ALLOCATE_ALL |
| return false; |
| #endif |
| return APInt::getNumWords(llvm::APFloatBase::getSizeInBits(Sem)) == 1; |
| } |
| const llvm::fltSemantics &getSemantics() const { |
| return llvm::APFloatBase::EnumToSemantics(Semantics); |
| } |
| |
| void copy(const APFloat &F) { |
| if (singleWord()) { |
| Val = F.bitcastToAPInt().getZExtValue(); |
| } else { |
| assert(Memory); |
| std::memcpy(Memory, F.bitcastToAPInt().getRawData(), |
| numWords() * sizeof(uint64_t)); |
| } |
| } |
| |
| void take(uint64_t *NewMemory) { |
| if (singleWord()) |
| return; |
| |
| if (Memory) |
| std::memcpy(NewMemory, Memory, numWords() * sizeof(uint64_t)); |
| Memory = NewMemory; |
| } |
| |
| bool isSigned() const { return true; } |
| bool isNegative() const { return getValue().isNegative(); } |
| bool isZero() const { return getValue().isZero(); } |
| bool isNonZero() const { return getValue().isNonZero(); } |
| bool isMin() const { return getValue().isSmallest(); } |
| bool isMinusOne() const { return getValue().isExactlyValue(-1.0); } |
| bool isNan() const { return getValue().isNaN(); } |
| bool isSignaling() const { return getValue().isSignaling(); } |
| bool isInf() const { return getValue().isInfinity(); } |
| bool isFinite() const { return getValue().isFinite(); } |
| bool isNormal() const { return getValue().isNormal(); } |
| bool isDenormal() const { return getValue().isDenormal(); } |
| llvm::FPClassTest classify() const { return getValue().classify(); } |
| APFloat::fltCategory getCategory() const { return getValue().getCategory(); } |
| |
| ComparisonCategoryResult compare(const Floating &RHS) const { |
| llvm::APFloatBase::cmpResult CmpRes = getValue().compare(RHS.getValue()); |
| switch (CmpRes) { |
| case llvm::APFloatBase::cmpLessThan: |
| return ComparisonCategoryResult::Less; |
| case llvm::APFloatBase::cmpEqual: |
| return ComparisonCategoryResult::Equal; |
| case llvm::APFloatBase::cmpGreaterThan: |
| return ComparisonCategoryResult::Greater; |
| case llvm::APFloatBase::cmpUnordered: |
| return ComparisonCategoryResult::Unordered; |
| } |
| llvm_unreachable("Inavlid cmpResult value"); |
| } |
| |
| static APFloat::opStatus fromIntegral(APSInt Val, |
| const llvm::fltSemantics &Sem, |
| llvm::RoundingMode RM, |
| Floating *Result) { |
| APFloat F = APFloat(Sem); |
| APFloat::opStatus Status = F.convertFromAPInt(Val, Val.isSigned(), RM); |
| Result->copy(F); |
| return Status; |
| } |
| |
| static void bitcastFromMemory(const std::byte *Buff, |
| const llvm::fltSemantics &Sem, |
| Floating *Result) { |
| size_t Size = APFloat::semanticsSizeInBits(Sem); |
| llvm::APInt API(Size, true); |
| llvm::LoadIntFromMemory(API, (const uint8_t *)Buff, Size / 8); |
| Result->copy(APFloat(Sem, API)); |
| } |
| |
| void bitcastToMemory(std::byte *Buff) const { |
| llvm::APInt API = getValue().bitcastToAPInt(); |
| llvm::StoreIntToMemory(API, (uint8_t *)Buff, bitWidth() / 8); |
| } |
| |
| // === Serialization support === |
| size_t bytesToSerialize() const { |
| return sizeof(Semantics) + (numWords() * sizeof(uint64_t)); |
| } |
| |
| void serialize(std::byte *Buff) const { |
| std::memcpy(Buff, &Semantics, sizeof(Semantics)); |
| if (singleWord()) { |
| std::memcpy(Buff + sizeof(Semantics), &Val, sizeof(uint64_t)); |
| } else { |
| std::memcpy(Buff + sizeof(Semantics), Memory, |
| numWords() * sizeof(uint64_t)); |
| } |
| } |
| |
| static llvm::APFloatBase::Semantics |
| deserializeSemantics(const std::byte *Buff) { |
| return *reinterpret_cast<const llvm::APFloatBase::Semantics *>(Buff); |
| } |
| |
| static void deserialize(const std::byte *Buff, Floating *Result) { |
| llvm::APFloatBase::Semantics Semantics; |
| std::memcpy(&Semantics, Buff, sizeof(Semantics)); |
| |
| unsigned BitWidth = llvm::APFloat::semanticsSizeInBits( |
| llvm::APFloatBase::EnumToSemantics(Semantics)); |
| unsigned NumWords = llvm::APInt::getNumWords(BitWidth); |
| |
| Result->Semantics = Semantics; |
| if (NumWords == 1 && !ALLOCATE_ALL) { |
| std::memcpy(&Result->Val, Buff + sizeof(Semantics), sizeof(uint64_t)); |
| } else { |
| assert(Result->Memory); |
| std::memcpy(Result->Memory, Buff + sizeof(Semantics), |
| NumWords * sizeof(uint64_t)); |
| } |
| } |
| |
| // ------- |
| |
| static APFloat::opStatus add(const Floating &A, const Floating &B, |
| llvm::RoundingMode RM, Floating *R) { |
| APFloat LHS = A.getValue(); |
| APFloat RHS = B.getValue(); |
| |
| auto Status = LHS.add(RHS, RM); |
| R->copy(LHS); |
| return Status; |
| } |
| |
| static APFloat::opStatus increment(const Floating &A, llvm::RoundingMode RM, |
| Floating *R) { |
| APFloat One(A.getSemantics(), 1); |
| APFloat LHS = A.getValue(); |
| |
| auto Status = LHS.add(One, RM); |
| R->copy(LHS); |
| return Status; |
| } |
| |
| static APFloat::opStatus sub(const Floating &A, const Floating &B, |
| llvm::RoundingMode RM, Floating *R) { |
| APFloat LHS = A.getValue(); |
| APFloat RHS = B.getValue(); |
| |
| auto Status = LHS.subtract(RHS, RM); |
| R->copy(LHS); |
| return Status; |
| } |
| |
| static APFloat::opStatus decrement(const Floating &A, llvm::RoundingMode RM, |
| Floating *R) { |
| APFloat One(A.getSemantics(), 1); |
| APFloat LHS = A.getValue(); |
| |
| auto Status = LHS.subtract(One, RM); |
| R->copy(LHS); |
| return Status; |
| } |
| |
| static APFloat::opStatus mul(const Floating &A, const Floating &B, |
| llvm::RoundingMode RM, Floating *R) { |
| |
| APFloat LHS = A.getValue(); |
| APFloat RHS = B.getValue(); |
| |
| auto Status = LHS.multiply(RHS, RM); |
| R->copy(LHS); |
| return Status; |
| } |
| |
| static APFloat::opStatus div(const Floating &A, const Floating &B, |
| llvm::RoundingMode RM, Floating *R) { |
| APFloat LHS = A.getValue(); |
| APFloat RHS = B.getValue(); |
| |
| auto Status = LHS.divide(RHS, RM); |
| R->copy(LHS); |
| return Status; |
| } |
| |
| static bool neg(const Floating &A, Floating *R) { |
| R->copy(-A.getValue()); |
| return false; |
| } |
| }; |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F); |
| Floating getSwappedBytes(Floating F); |
| |
| } // namespace interp |
| } // namespace clang |
| |
| #endif |