| 1 | //===--- Floating.h - Types for the constexpr VM ----------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Defines the VM types and helpers operating on types. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #ifndef LLVM_CLANG_AST_INTERP_FLOATING_H |
| 14 | #define LLVM_CLANG_AST_INTERP_FLOATING_H |
| 15 | |
| 16 | #include "Primitives.h" |
| 17 | #include "clang/AST/APValue.h" |
| 18 | #include "llvm/ADT/APFloat.h" |
| 19 | |
| 20 | // XXX This is just a debugging help. Setting this to 1 will heap-allocate ALL |
| 21 | // floating values. |
| 22 | #define ALLOCATE_ALL 0 |
| 23 | |
| 24 | namespace clang { |
| 25 | namespace interp { |
| 26 | |
| 27 | using APFloat = llvm::APFloat; |
| 28 | using APSInt = llvm::APSInt; |
| 29 | using APInt = llvm::APInt; |
| 30 | |
| 31 | /// If a Floating is constructed from Memory, it DOES NOT OWN THAT MEMORY. |
| 32 | /// It will NOT copy the memory (unless, of course, copy() is called) and it |
| 33 | /// won't alllocate anything. The allocation should happen via InterpState or |
| 34 | /// Program. |
| 35 | class Floating final { |
| 36 | private: |
| 37 | union { |
| 38 | uint64_t Val = 0; |
| 39 | uint64_t *Memory; |
| 40 | }; |
| 41 | llvm::APFloatBase::Semantics Semantics; |
| 42 | |
| 43 | APFloat getValue() const { |
| 44 | unsigned BitWidth = bitWidth(); |
| 45 | if (singleWord()) |
| 46 | return APFloat(getSemantics(), APInt(BitWidth, Val)); |
| 47 | unsigned NumWords = numWords(); |
| 48 | return APFloat(getSemantics(), APInt(BitWidth, NumWords, Memory)); |
| 49 | } |
| 50 | |
| 51 | public: |
| 52 | Floating() = default; |
| 53 | Floating(llvm::APFloatBase::Semantics Semantics) |
| 54 | : Val(0), Semantics(Semantics) {} |
| 55 | Floating(const APFloat &F) { |
| 56 | |
| 57 | Semantics = llvm::APFloatBase::SemanticsToEnum(Sem: F.getSemantics()); |
| 58 | this->copy(F); |
| 59 | } |
| 60 | Floating(uint64_t *Memory, llvm::APFloatBase::Semantics Semantics) |
| 61 | : Memory(Memory), Semantics(Semantics) {} |
| 62 | |
| 63 | APFloat getAPFloat() const { return getValue(); } |
| 64 | |
| 65 | bool operator<(Floating RHS) const { return getValue() < RHS.getValue(); } |
| 66 | bool operator>(Floating RHS) const { return getValue() > RHS.getValue(); } |
| 67 | bool operator<=(Floating RHS) const { return getValue() <= RHS.getValue(); } |
| 68 | bool operator>=(Floating RHS) const { return getValue() >= RHS.getValue(); } |
| 69 | |
| 70 | APFloat::opStatus convertToInteger(APSInt &Result) const { |
| 71 | bool IsExact; |
| 72 | return getValue().convertToInteger(Result, RM: llvm::APFloat::rmTowardZero, |
| 73 | IsExact: &IsExact); |
| 74 | } |
| 75 | |
| 76 | void toSemantics(const llvm::fltSemantics *Sem, llvm::RoundingMode RM, |
| 77 | Floating *Result) const { |
| 78 | APFloat Copy = getValue(); |
| 79 | bool LosesInfo; |
| 80 | Copy.convert(ToSemantics: *Sem, RM, losesInfo: &LosesInfo); |
| 81 | (void)LosesInfo; |
| 82 | Result->copy(F: Copy); |
| 83 | } |
| 84 | |
| 85 | APSInt toAPSInt(unsigned NumBits = 0) const { |
| 86 | return APSInt(getValue().bitcastToAPInt()); |
| 87 | } |
| 88 | APValue toAPValue(const ASTContext &) const { return APValue(getValue()); } |
| 89 | void print(llvm::raw_ostream &OS) const { |
| 90 | // Can't use APFloat::print() since it appends a newline. |
| 91 | SmallVector<char, 16> Buffer; |
| 92 | getValue().toString(Str&: Buffer); |
| 93 | OS << Buffer; |
| 94 | } |
| 95 | std::string toDiagnosticString(const ASTContext &Ctx) const { |
| 96 | std::string NameStr; |
| 97 | llvm::raw_string_ostream OS(NameStr); |
| 98 | print(OS); |
| 99 | return NameStr; |
| 100 | } |
| 101 | |
| 102 | unsigned bitWidth() const { |
| 103 | return llvm::APFloatBase::semanticsSizeInBits(getSemantics()); |
| 104 | } |
| 105 | unsigned numWords() const { return llvm::APInt::getNumWords(BitWidth: bitWidth()); } |
| 106 | bool singleWord() const { |
| 107 | #if ALLOCATE_ALL |
| 108 | return false; |
| 109 | #endif |
| 110 | return numWords() == 1; |
| 111 | } |
| 112 | static bool singleWord(const llvm::fltSemantics &Sem) { |
| 113 | #if ALLOCATE_ALL |
| 114 | return false; |
| 115 | #endif |
| 116 | return APInt::getNumWords(BitWidth: llvm::APFloatBase::getSizeInBits(Sem)) == 1; |
| 117 | } |
| 118 | const llvm::fltSemantics &getSemantics() const { |
| 119 | return llvm::APFloatBase::EnumToSemantics(S: Semantics); |
| 120 | } |
| 121 | |
| 122 | void copy(const APFloat &F) { |
| 123 | if (singleWord()) { |
| 124 | Val = F.bitcastToAPInt().getZExtValue(); |
| 125 | } else { |
| 126 | assert(Memory); |
| 127 | std::memcpy(dest: Memory, src: F.bitcastToAPInt().getRawData(), |
| 128 | n: numWords() * sizeof(uint64_t)); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | void take(uint64_t *NewMemory) { |
| 133 | if (singleWord()) |
| 134 | return; |
| 135 | |
| 136 | if (Memory) |
| 137 | std::memcpy(dest: NewMemory, src: Memory, n: numWords() * sizeof(uint64_t)); |
| 138 | Memory = NewMemory; |
| 139 | } |
| 140 | |
| 141 | bool isSigned() const { return true; } |
| 142 | bool isNegative() const { return getValue().isNegative(); } |
| 143 | bool isZero() const { return getValue().isZero(); } |
| 144 | bool isNonZero() const { return getValue().isNonZero(); } |
| 145 | bool isMin() const { return getValue().isSmallest(); } |
| 146 | bool isMinusOne() const { return getValue().isExactlyValue(V: -1.0); } |
| 147 | bool isNan() const { return getValue().isNaN(); } |
| 148 | bool isSignaling() const { return getValue().isSignaling(); } |
| 149 | bool isInf() const { return getValue().isInfinity(); } |
| 150 | bool isFinite() const { return getValue().isFinite(); } |
| 151 | bool isNormal() const { return getValue().isNormal(); } |
| 152 | bool isDenormal() const { return getValue().isDenormal(); } |
| 153 | llvm::FPClassTest classify() const { return getValue().classify(); } |
| 154 | APFloat::fltCategory getCategory() const { return getValue().getCategory(); } |
| 155 | |
| 156 | ComparisonCategoryResult compare(const Floating &RHS) const { |
| 157 | llvm::APFloatBase::cmpResult CmpRes = getValue().compare(RHS: RHS.getValue()); |
| 158 | switch (CmpRes) { |
| 159 | case llvm::APFloatBase::cmpLessThan: |
| 160 | return ComparisonCategoryResult::Less; |
| 161 | case llvm::APFloatBase::cmpEqual: |
| 162 | return ComparisonCategoryResult::Equal; |
| 163 | case llvm::APFloatBase::cmpGreaterThan: |
| 164 | return ComparisonCategoryResult::Greater; |
| 165 | case llvm::APFloatBase::cmpUnordered: |
| 166 | return ComparisonCategoryResult::Unordered; |
| 167 | } |
| 168 | llvm_unreachable("Inavlid cmpResult value" ); |
| 169 | } |
| 170 | |
| 171 | static APFloat::opStatus fromIntegral(APSInt Val, |
| 172 | const llvm::fltSemantics &Sem, |
| 173 | llvm::RoundingMode RM, |
| 174 | Floating *Result) { |
| 175 | APFloat F = APFloat(Sem); |
| 176 | APFloat::opStatus Status = F.convertFromAPInt(Input: Val, IsSigned: Val.isSigned(), RM); |
| 177 | Result->copy(F); |
| 178 | return Status; |
| 179 | } |
| 180 | |
| 181 | static void bitcastFromMemory(const std::byte *Buff, |
| 182 | const llvm::fltSemantics &Sem, |
| 183 | Floating *Result) { |
| 184 | size_t Size = APFloat::semanticsSizeInBits(Sem); |
| 185 | llvm::APInt API(Size, true); |
| 186 | llvm::LoadIntFromMemory(IntVal&: API, Src: (const uint8_t *)Buff, LoadBytes: Size / 8); |
| 187 | Result->copy(F: APFloat(Sem, API)); |
| 188 | } |
| 189 | |
| 190 | void bitcastToMemory(std::byte *Buff) const { |
| 191 | llvm::APInt API = getValue().bitcastToAPInt(); |
| 192 | llvm::StoreIntToMemory(IntVal: API, Dst: (uint8_t *)Buff, StoreBytes: bitWidth() / 8); |
| 193 | } |
| 194 | |
| 195 | // === Serialization support === |
| 196 | size_t bytesToSerialize() const { |
| 197 | return sizeof(Semantics) + (numWords() * sizeof(uint64_t)); |
| 198 | } |
| 199 | |
| 200 | void serialize(std::byte *Buff) const { |
| 201 | std::memcpy(dest: Buff, src: &Semantics, n: sizeof(Semantics)); |
| 202 | if (singleWord()) { |
| 203 | std::memcpy(dest: Buff + sizeof(Semantics), src: &Val, n: sizeof(uint64_t)); |
| 204 | } else { |
| 205 | std::memcpy(dest: Buff + sizeof(Semantics), src: Memory, |
| 206 | n: numWords() * sizeof(uint64_t)); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | static llvm::APFloatBase::Semantics |
| 211 | deserializeSemantics(const std::byte *Buff) { |
| 212 | return *reinterpret_cast<const llvm::APFloatBase::Semantics *>(Buff); |
| 213 | } |
| 214 | |
| 215 | static void deserialize(const std::byte *Buff, Floating *Result) { |
| 216 | llvm::APFloatBase::Semantics Semantics; |
| 217 | std::memcpy(dest: &Semantics, src: Buff, n: sizeof(Semantics)); |
| 218 | |
| 219 | unsigned BitWidth = llvm::APFloat::semanticsSizeInBits( |
| 220 | llvm::APFloatBase::EnumToSemantics(S: Semantics)); |
| 221 | unsigned NumWords = llvm::APInt::getNumWords(BitWidth); |
| 222 | |
| 223 | Result->Semantics = Semantics; |
| 224 | if (NumWords == 1 && !ALLOCATE_ALL) { |
| 225 | std::memcpy(dest: &Result->Val, src: Buff + sizeof(Semantics), n: sizeof(uint64_t)); |
| 226 | } else { |
| 227 | assert(Result->Memory); |
| 228 | std::memcpy(dest: Result->Memory, src: Buff + sizeof(Semantics), |
| 229 | n: NumWords * sizeof(uint64_t)); |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | // ------- |
| 234 | |
| 235 | static APFloat::opStatus add(const Floating &A, const Floating &B, |
| 236 | llvm::RoundingMode RM, Floating *R) { |
| 237 | APFloat LHS = A.getValue(); |
| 238 | APFloat RHS = B.getValue(); |
| 239 | |
| 240 | auto Status = LHS.add(RHS, RM); |
| 241 | R->copy(F: LHS); |
| 242 | return Status; |
| 243 | } |
| 244 | |
| 245 | static APFloat::opStatus increment(const Floating &A, llvm::RoundingMode RM, |
| 246 | Floating *R) { |
| 247 | APFloat One(A.getSemantics(), 1); |
| 248 | APFloat LHS = A.getValue(); |
| 249 | |
| 250 | auto Status = LHS.add(RHS: One, RM); |
| 251 | R->copy(F: LHS); |
| 252 | return Status; |
| 253 | } |
| 254 | |
| 255 | static APFloat::opStatus sub(const Floating &A, const Floating &B, |
| 256 | llvm::RoundingMode RM, Floating *R) { |
| 257 | APFloat LHS = A.getValue(); |
| 258 | APFloat RHS = B.getValue(); |
| 259 | |
| 260 | auto Status = LHS.subtract(RHS, RM); |
| 261 | R->copy(F: LHS); |
| 262 | return Status; |
| 263 | } |
| 264 | |
| 265 | static APFloat::opStatus decrement(const Floating &A, llvm::RoundingMode RM, |
| 266 | Floating *R) { |
| 267 | APFloat One(A.getSemantics(), 1); |
| 268 | APFloat LHS = A.getValue(); |
| 269 | |
| 270 | auto Status = LHS.subtract(RHS: One, RM); |
| 271 | R->copy(F: LHS); |
| 272 | return Status; |
| 273 | } |
| 274 | |
| 275 | static APFloat::opStatus mul(const Floating &A, const Floating &B, |
| 276 | llvm::RoundingMode RM, Floating *R) { |
| 277 | |
| 278 | APFloat LHS = A.getValue(); |
| 279 | APFloat RHS = B.getValue(); |
| 280 | |
| 281 | auto Status = LHS.multiply(RHS, RM); |
| 282 | R->copy(F: LHS); |
| 283 | return Status; |
| 284 | } |
| 285 | |
| 286 | static APFloat::opStatus div(const Floating &A, const Floating &B, |
| 287 | llvm::RoundingMode RM, Floating *R) { |
| 288 | APFloat LHS = A.getValue(); |
| 289 | APFloat RHS = B.getValue(); |
| 290 | |
| 291 | auto Status = LHS.divide(RHS, RM); |
| 292 | R->copy(F: LHS); |
| 293 | return Status; |
| 294 | } |
| 295 | |
| 296 | static bool neg(const Floating &A, Floating *R) { |
| 297 | R->copy(F: -A.getValue()); |
| 298 | return false; |
| 299 | } |
| 300 | }; |
| 301 | |
| 302 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F); |
| 303 | Floating getSwappedBytes(Floating F); |
| 304 | |
| 305 | } // namespace interp |
| 306 | } // namespace clang |
| 307 | |
| 308 | #endif |
| 309 | |