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 | |