| 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(), |
| 49 | APInt(BitWidth, llvm::ArrayRef(Memory, NumWords))); |
| 50 | } |
| 51 | |
| 52 | public: |
| 53 | Floating() = default; |
| 54 | Floating(llvm::APFloatBase::Semantics Semantics) |
| 55 | : Val(0), Semantics(Semantics) {} |
| 56 | Floating(const APFloat &F) { |
| 57 | |
| 58 | Semantics = llvm::APFloatBase::SemanticsToEnum(Sem: F.getSemantics()); |
| 59 | this->copy(F); |
| 60 | } |
| 61 | Floating(uint64_t *Memory, llvm::APFloatBase::Semantics Semantics) |
| 62 | : Memory(Memory), Semantics(Semantics) {} |
| 63 | |
| 64 | APFloat getAPFloat() const { return getValue(); } |
| 65 | |
| 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 | bool operator>=(Floating RHS) const { return getValue() >= RHS.getValue(); } |
| 70 | |
| 71 | APFloat::opStatus convertToInteger(APSInt &Result) const { |
| 72 | bool IsExact; |
| 73 | return getValue().convertToInteger(Result, RM: llvm::APFloat::rmTowardZero, |
| 74 | IsExact: &IsExact); |
| 75 | } |
| 76 | |
| 77 | void toSemantics(const llvm::fltSemantics *Sem, llvm::RoundingMode RM, |
| 78 | Floating *Result) const { |
| 79 | APFloat Copy = getValue(); |
| 80 | bool LosesInfo; |
| 81 | Copy.convert(ToSemantics: *Sem, RM, losesInfo: &LosesInfo); |
| 82 | (void)LosesInfo; |
| 83 | Result->copy(F: Copy); |
| 84 | } |
| 85 | |
| 86 | APSInt toAPSInt(unsigned NumBits = 0) const { |
| 87 | return APSInt(getValue().bitcastToAPInt()); |
| 88 | } |
| 89 | APValue toAPValue(const ASTContext &) const { return APValue(getValue()); } |
| 90 | void print(llvm::raw_ostream &OS) const { |
| 91 | // Can't use APFloat::print() since it appends a newline. |
| 92 | SmallVector<char, 16> Buffer; |
| 93 | getValue().toString(Str&: Buffer); |
| 94 | OS << Buffer; |
| 95 | } |
| 96 | std::string toDiagnosticString(const ASTContext &Ctx) const { |
| 97 | std::string NameStr; |
| 98 | llvm::raw_string_ostream OS(NameStr); |
| 99 | print(OS); |
| 100 | return NameStr; |
| 101 | } |
| 102 | |
| 103 | unsigned bitWidth() const { |
| 104 | return llvm::APFloatBase::semanticsSizeInBits(getSemantics()); |
| 105 | } |
| 106 | unsigned numWords() const { return llvm::APInt::getNumWords(BitWidth: bitWidth()); } |
| 107 | bool singleWord() const { |
| 108 | #if ALLOCATE_ALL |
| 109 | return false; |
| 110 | #endif |
| 111 | return numWords() == 1; |
| 112 | } |
| 113 | static bool singleWord(const llvm::fltSemantics &Sem) { |
| 114 | #if ALLOCATE_ALL |
| 115 | return false; |
| 116 | #endif |
| 117 | return APInt::getNumWords(BitWidth: llvm::APFloatBase::getSizeInBits(Sem)) == 1; |
| 118 | } |
| 119 | const llvm::fltSemantics &getSemantics() const { |
| 120 | return llvm::APFloatBase::EnumToSemantics(S: Semantics); |
| 121 | } |
| 122 | |
| 123 | void copy(const APFloat &F) { |
| 124 | if (singleWord()) { |
| 125 | Val = F.bitcastToAPInt().getZExtValue(); |
| 126 | } else { |
| 127 | assert(Memory); |
| 128 | std::memcpy(dest: Memory, src: F.bitcastToAPInt().getRawData(), |
| 129 | n: numWords() * sizeof(uint64_t)); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | void take(uint64_t *NewMemory) { |
| 134 | if (singleWord()) |
| 135 | return; |
| 136 | |
| 137 | if (Memory) |
| 138 | std::memcpy(dest: NewMemory, src: Memory, n: numWords() * sizeof(uint64_t)); |
| 139 | Memory = NewMemory; |
| 140 | } |
| 141 | |
| 142 | bool isSigned() const { return true; } |
| 143 | bool isNegative() const { return getValue().isNegative(); } |
| 144 | bool isZero() const { return getValue().isZero(); } |
| 145 | bool isNonZero() const { return getValue().isNonZero(); } |
| 146 | bool isMin() const { return getValue().isSmallest(); } |
| 147 | bool isMinusOne() const { return getValue().isExactlyValue(V: -1.0); } |
| 148 | bool isNan() const { return getValue().isNaN(); } |
| 149 | bool isSignaling() const { return getValue().isSignaling(); } |
| 150 | bool isInf() const { return getValue().isInfinity(); } |
| 151 | bool isFinite() const { return getValue().isFinite(); } |
| 152 | bool isNormal() const { return getValue().isNormal(); } |
| 153 | bool isDenormal() const { return getValue().isDenormal(); } |
| 154 | llvm::FPClassTest classify() const { return getValue().classify(); } |
| 155 | APFloat::fltCategory getCategory() const { return getValue().getCategory(); } |
| 156 | |
| 157 | ComparisonCategoryResult compare(const Floating &RHS) const { |
| 158 | llvm::APFloatBase::cmpResult CmpRes = getValue().compare(RHS: RHS.getValue()); |
| 159 | switch (CmpRes) { |
| 160 | case llvm::APFloatBase::cmpLessThan: |
| 161 | return ComparisonCategoryResult::Less; |
| 162 | case llvm::APFloatBase::cmpEqual: |
| 163 | return ComparisonCategoryResult::Equal; |
| 164 | case llvm::APFloatBase::cmpGreaterThan: |
| 165 | return ComparisonCategoryResult::Greater; |
| 166 | case llvm::APFloatBase::cmpUnordered: |
| 167 | return ComparisonCategoryResult::Unordered; |
| 168 | } |
| 169 | llvm_unreachable("Inavlid cmpResult value" ); |
| 170 | } |
| 171 | |
| 172 | static APFloat::opStatus fromIntegral(APSInt Val, |
| 173 | const llvm::fltSemantics &Sem, |
| 174 | llvm::RoundingMode RM, |
| 175 | Floating *Result) { |
| 176 | APFloat F = APFloat(Sem); |
| 177 | APFloat::opStatus Status = F.convertFromAPInt(Input: Val, IsSigned: Val.isSigned(), RM); |
| 178 | Result->copy(F); |
| 179 | return Status; |
| 180 | } |
| 181 | |
| 182 | static void bitcastFromMemory(const std::byte *Buff, |
| 183 | const llvm::fltSemantics &Sem, |
| 184 | Floating *Result) { |
| 185 | size_t Size = APFloat::semanticsSizeInBits(Sem); |
| 186 | llvm::APInt API(Size, true); |
| 187 | llvm::LoadIntFromMemory(IntVal&: API, Src: (const uint8_t *)Buff, LoadBytes: Size / 8); |
| 188 | Result->copy(F: APFloat(Sem, API)); |
| 189 | } |
| 190 | |
| 191 | void bitcastToMemory(std::byte *Buff) const { |
| 192 | llvm::APInt API = getValue().bitcastToAPInt(); |
| 193 | llvm::StoreIntToMemory(IntVal: API, Dst: (uint8_t *)Buff, StoreBytes: bitWidth() / 8); |
| 194 | } |
| 195 | |
| 196 | // === Serialization support === |
| 197 | size_t bytesToSerialize() const { |
| 198 | return sizeof(Semantics) + (numWords() * sizeof(uint64_t)); |
| 199 | } |
| 200 | |
| 201 | void serialize(std::byte *Buff) const { |
| 202 | std::memcpy(dest: Buff, src: &Semantics, n: sizeof(Semantics)); |
| 203 | if (singleWord()) { |
| 204 | std::memcpy(dest: Buff + sizeof(Semantics), src: &Val, n: sizeof(uint64_t)); |
| 205 | } else { |
| 206 | std::memcpy(dest: Buff + sizeof(Semantics), src: Memory, |
| 207 | n: numWords() * sizeof(uint64_t)); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | static llvm::APFloatBase::Semantics |
| 212 | deserializeSemantics(const std::byte *Buff) { |
| 213 | return *reinterpret_cast<const llvm::APFloatBase::Semantics *>(Buff); |
| 214 | } |
| 215 | |
| 216 | static void deserialize(const std::byte *Buff, Floating *Result) { |
| 217 | llvm::APFloatBase::Semantics Semantics; |
| 218 | std::memcpy(dest: &Semantics, src: Buff, n: sizeof(Semantics)); |
| 219 | |
| 220 | unsigned BitWidth = llvm::APFloat::semanticsSizeInBits( |
| 221 | llvm::APFloatBase::EnumToSemantics(S: Semantics)); |
| 222 | unsigned NumWords = llvm::APInt::getNumWords(BitWidth); |
| 223 | |
| 224 | Result->Semantics = Semantics; |
| 225 | if (NumWords == 1 && !ALLOCATE_ALL) { |
| 226 | std::memcpy(dest: &Result->Val, src: Buff + sizeof(Semantics), n: sizeof(uint64_t)); |
| 227 | } else { |
| 228 | assert(Result->Memory); |
| 229 | std::memcpy(dest: Result->Memory, src: Buff + sizeof(Semantics), |
| 230 | n: NumWords * sizeof(uint64_t)); |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | // ------- |
| 235 | |
| 236 | static APFloat::opStatus add(const Floating &A, const Floating &B, |
| 237 | llvm::RoundingMode RM, Floating *R) { |
| 238 | APFloat LHS = A.getValue(); |
| 239 | APFloat RHS = B.getValue(); |
| 240 | |
| 241 | auto Status = LHS.add(RHS, RM); |
| 242 | R->copy(F: LHS); |
| 243 | return Status; |
| 244 | } |
| 245 | |
| 246 | static APFloat::opStatus increment(const Floating &A, llvm::RoundingMode RM, |
| 247 | Floating *R) { |
| 248 | APFloat One(A.getSemantics(), 1); |
| 249 | APFloat LHS = A.getValue(); |
| 250 | |
| 251 | auto Status = LHS.add(RHS: One, RM); |
| 252 | R->copy(F: LHS); |
| 253 | return Status; |
| 254 | } |
| 255 | |
| 256 | static APFloat::opStatus sub(const Floating &A, const Floating &B, |
| 257 | llvm::RoundingMode RM, Floating *R) { |
| 258 | APFloat LHS = A.getValue(); |
| 259 | APFloat RHS = B.getValue(); |
| 260 | |
| 261 | auto Status = LHS.subtract(RHS, RM); |
| 262 | R->copy(F: LHS); |
| 263 | return Status; |
| 264 | } |
| 265 | |
| 266 | static APFloat::opStatus decrement(const Floating &A, llvm::RoundingMode RM, |
| 267 | Floating *R) { |
| 268 | APFloat One(A.getSemantics(), 1); |
| 269 | APFloat LHS = A.getValue(); |
| 270 | |
| 271 | auto Status = LHS.subtract(RHS: One, RM); |
| 272 | R->copy(F: LHS); |
| 273 | return Status; |
| 274 | } |
| 275 | |
| 276 | static APFloat::opStatus mul(const Floating &A, const Floating &B, |
| 277 | llvm::RoundingMode RM, Floating *R) { |
| 278 | |
| 279 | APFloat LHS = A.getValue(); |
| 280 | APFloat RHS = B.getValue(); |
| 281 | |
| 282 | auto Status = LHS.multiply(RHS, RM); |
| 283 | R->copy(F: LHS); |
| 284 | return Status; |
| 285 | } |
| 286 | |
| 287 | static APFloat::opStatus div(const Floating &A, const Floating &B, |
| 288 | llvm::RoundingMode RM, Floating *R) { |
| 289 | APFloat LHS = A.getValue(); |
| 290 | APFloat RHS = B.getValue(); |
| 291 | |
| 292 | auto Status = LHS.divide(RHS, RM); |
| 293 | R->copy(F: LHS); |
| 294 | return Status; |
| 295 | } |
| 296 | |
| 297 | static bool neg(const Floating &A, Floating *R) { |
| 298 | R->copy(F: -A.getValue()); |
| 299 | return false; |
| 300 | } |
| 301 | }; |
| 302 | |
| 303 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F); |
| 304 | Floating getSwappedBytes(Floating F); |
| 305 | |
| 306 | } // namespace interp |
| 307 | } // namespace clang |
| 308 | |
| 309 | #endif |
| 310 | |