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(),
49 APInt(BitWidth, llvm::ArrayRef(Memory, NumWords)));
50 }
51
52public:
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
303llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F);
304Floating getSwappedBytes(Floating F);
305
306} // namespace interp
307} // namespace clang
308
309#endif
310