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
24namespace clang {
25namespace interp {
26
27using APFloat = llvm::APFloat;
28using APSInt = llvm::APSInt;
29using 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.
35class Floating final {
36private:
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
51public:
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
302llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F);
303Floating getSwappedBytes(Floating F);
304
305} // namespace interp
306} // namespace clang
307
308#endif
309