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
20namespace clang {
21namespace interp {
22
23using APFloat = llvm::APFloat;
24using APSInt = llvm::APSInt;
25
26class Floating final {
27private:
28 // The underlying value storage.
29 APFloat F;
30
31public:
32 /// Zero-initializes a Floating.
33 Floating() : F(0.0f) {}
34 Floating(const APFloat &F) : F(F) {}
35
36 // Static constructors for special floating point values.
37 static Floating getInf(const llvm::fltSemantics &Sem) {
38 return Floating(APFloat::getInf(Sem));
39 }
40 const APFloat &getAPFloat() const { return F; }
41
42 bool operator<(Floating RHS) const { return F < RHS.F; }
43 bool operator>(Floating RHS) const { return F > RHS.F; }
44 bool operator<=(Floating RHS) const { return F <= RHS.F; }
45 bool operator>=(Floating RHS) const { return F >= RHS.F; }
46 bool operator==(Floating RHS) const { return F == RHS.F; }
47 bool operator!=(Floating RHS) const { return F != RHS.F; }
48 Floating operator-() const { return Floating(-F); }
49
50 APFloat::opStatus convertToInteger(APSInt &Result) const {
51 bool IsExact;
52 return F.convertToInteger(Result, RM: llvm::APFloat::rmTowardZero, IsExact: &IsExact);
53 }
54
55 Floating toSemantics(const llvm::fltSemantics *Sem,
56 llvm::RoundingMode RM) const {
57 APFloat Copy = F;
58 bool LosesInfo;
59 Copy.convert(ToSemantics: *Sem, RM, losesInfo: &LosesInfo);
60 (void)LosesInfo;
61 return Floating(Copy);
62 }
63
64 /// Convert this Floating to one with the same semantics as \Other.
65 Floating toSemantics(const Floating &Other, llvm::RoundingMode RM) const {
66 return toSemantics(Sem: &Other.F.getSemantics(), RM);
67 }
68
69 APSInt toAPSInt(unsigned NumBits = 0) const {
70 return APSInt(F.bitcastToAPInt());
71 }
72 APValue toAPValue(const ASTContext &) const { return APValue(F); }
73 void print(llvm::raw_ostream &OS) const {
74 // Can't use APFloat::print() since it appends a newline.
75 SmallVector<char, 16> Buffer;
76 F.toString(Str&: Buffer);
77 OS << Buffer;
78 }
79 std::string toDiagnosticString(const ASTContext &Ctx) const {
80 std::string NameStr;
81 llvm::raw_string_ostream OS(NameStr);
82 print(OS);
83 return NameStr;
84 }
85
86 unsigned bitWidth() const { return F.semanticsSizeInBits(F.getSemantics()); }
87
88 bool isSigned() const { return true; }
89 bool isNegative() const { return F.isNegative(); }
90 bool isPositive() const { return !F.isNegative(); }
91 bool isZero() const { return F.isZero(); }
92 bool isNonZero() const { return F.isNonZero(); }
93 bool isMin() const { return F.isSmallest(); }
94 bool isMinusOne() const { return F.isExactlyValue(V: -1.0); }
95 bool isNan() const { return F.isNaN(); }
96 bool isSignaling() const { return F.isSignaling(); }
97 bool isInf() const { return F.isInfinity(); }
98 bool isFinite() const { return F.isFinite(); }
99 bool isNormal() const { return F.isNormal(); }
100 bool isDenormal() const { return F.isDenormal(); }
101 llvm::FPClassTest classify() const { return F.classify(); }
102 APFloat::fltCategory getCategory() const { return F.getCategory(); }
103
104 ComparisonCategoryResult compare(const Floating &RHS) const {
105 llvm::APFloatBase::cmpResult CmpRes = F.compare(RHS: RHS.F);
106 switch (CmpRes) {
107 case llvm::APFloatBase::cmpLessThan:
108 return ComparisonCategoryResult::Less;
109 case llvm::APFloatBase::cmpEqual:
110 return ComparisonCategoryResult::Equal;
111 case llvm::APFloatBase::cmpGreaterThan:
112 return ComparisonCategoryResult::Greater;
113 case llvm::APFloatBase::cmpUnordered:
114 return ComparisonCategoryResult::Unordered;
115 }
116 llvm_unreachable("Inavlid cmpResult value");
117 }
118
119 static APFloat::opStatus fromIntegral(APSInt Val,
120 const llvm::fltSemantics &Sem,
121 llvm::RoundingMode RM,
122 Floating &Result) {
123 APFloat F = APFloat(Sem);
124 APFloat::opStatus Status = F.convertFromAPInt(Input: Val, IsSigned: Val.isSigned(), RM);
125 Result = Floating(F);
126 return Status;
127 }
128
129 static Floating bitcastFromMemory(const std::byte *Buff,
130 const llvm::fltSemantics &Sem) {
131 size_t Size = APFloat::semanticsSizeInBits(Sem);
132 llvm::APInt API(Size, true);
133 llvm::LoadIntFromMemory(IntVal&: API, Src: (const uint8_t *)Buff, LoadBytes: Size / 8);
134
135 return Floating(APFloat(Sem, API));
136 }
137
138 // === Serialization support ===
139 size_t bytesToSerialize() const {
140 return sizeof(llvm::fltSemantics *) +
141 (APFloat::semanticsSizeInBits(F.getSemantics()) / 8);
142 }
143
144 void serialize(std::byte *Buff) const {
145 // Semantics followed by an APInt.
146 *reinterpret_cast<const llvm::fltSemantics **>(Buff) = &F.getSemantics();
147
148 llvm::APInt API = F.bitcastToAPInt();
149 llvm::StoreIntToMemory(IntVal: API, Dst: (uint8_t *)(Buff + sizeof(void *)),
150 StoreBytes: bitWidth() / 8);
151 }
152
153 static Floating deserialize(const std::byte *Buff) {
154 const llvm::fltSemantics *Sem;
155 std::memcpy(dest: (void *)&Sem, src: Buff, n: sizeof(void *));
156 return bitcastFromMemory(Buff: Buff + sizeof(void *), Sem: *Sem);
157 }
158
159 static Floating abs(const Floating &F) {
160 APFloat V = F.F;
161 if (V.isNegative())
162 V.changeSign();
163 return Floating(V);
164 }
165
166 // -------
167
168 static APFloat::opStatus add(const Floating &A, const Floating &B,
169 llvm::RoundingMode RM, Floating *R) {
170 *R = Floating(A.F);
171 return R->F.add(RHS: B.F, RM);
172 }
173
174 static APFloat::opStatus increment(const Floating &A, llvm::RoundingMode RM,
175 Floating *R) {
176 APFloat One(A.F.getSemantics(), 1);
177 *R = Floating(A.F);
178 return R->F.add(RHS: One, RM);
179 }
180
181 static APFloat::opStatus sub(const Floating &A, const Floating &B,
182 llvm::RoundingMode RM, Floating *R) {
183 *R = Floating(A.F);
184 return R->F.subtract(RHS: B.F, RM);
185 }
186
187 static APFloat::opStatus decrement(const Floating &A, llvm::RoundingMode RM,
188 Floating *R) {
189 APFloat One(A.F.getSemantics(), 1);
190 *R = Floating(A.F);
191 return R->F.subtract(RHS: One, RM);
192 }
193
194 static APFloat::opStatus mul(const Floating &A, const Floating &B,
195 llvm::RoundingMode RM, Floating *R) {
196 *R = Floating(A.F);
197 return R->F.multiply(RHS: B.F, RM);
198 }
199
200 static APFloat::opStatus div(const Floating &A, const Floating &B,
201 llvm::RoundingMode RM, Floating *R) {
202 *R = Floating(A.F);
203 return R->F.divide(RHS: B.F, RM);
204 }
205
206 static bool neg(const Floating &A, Floating *R) {
207 *R = -A;
208 return false;
209 }
210};
211
212llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F);
213Floating getSwappedBytes(Floating F);
214
215} // namespace interp
216} // namespace clang
217
218#endif
219