1//===--- PrimType.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_TYPE_H
14#define LLVM_CLANG_AST_INTERP_TYPE_H
15
16#include "llvm/Support/raw_ostream.h"
17#include <climits>
18#include <cstddef>
19#include <cstdint>
20
21namespace clang {
22namespace interp {
23
24class Pointer;
25class Boolean;
26class Floating;
27class MemberPointer;
28class FixedPoint;
29template <bool Signed> class IntegralAP;
30template <bool Signed> class Char;
31template <unsigned Bits, bool Signed> class Integral;
32
33/// Enumeration of the primitive types of the VM.
34enum PrimType : uint8_t {
35 PT_Sint8 = 0,
36 PT_Uint8 = 1,
37 PT_Sint16 = 2,
38 PT_Uint16 = 3,
39 PT_Sint32 = 4,
40 PT_Uint32 = 5,
41 PT_Sint64 = 6,
42 PT_Uint64 = 7,
43 PT_IntAP = 8,
44 PT_IntAPS = 9,
45 PT_Bool = 10,
46 PT_FixedPoint = 11,
47 PT_Float = 12,
48 PT_Ptr = 13,
49 PT_MemberPtr = 14,
50};
51
52constexpr bool isIntegerOrBoolType(PrimType T) { return T <= PT_Bool; }
53constexpr bool isIntegerType(PrimType T) { return T <= PT_IntAPS; }
54
55inline constexpr bool isPtrType(PrimType T) {
56 return T == PT_Ptr || T == PT_MemberPtr;
57}
58
59inline constexpr bool isSignedType(PrimType T) {
60 switch (T) {
61 case PT_Sint8:
62 case PT_Sint16:
63 case PT_Sint32:
64 case PT_Sint64:
65 return true;
66 default:
67 return false;
68 }
69 return false;
70}
71
72// Like std::optional<PrimType>, but only sizeof(PrimType).
73class OptPrimType final {
74 static constexpr uint8_t None = 0xFF;
75 uint8_t V = None;
76
77public:
78 OptPrimType() = default;
79 OptPrimType(std::nullopt_t) {}
80 OptPrimType(PrimType T) : V(static_cast<unsigned>(T)) {}
81
82 explicit constexpr operator bool() const { return V != None; }
83 PrimType operator*() const {
84 assert(operator bool());
85 return static_cast<PrimType>(V);
86 }
87
88 PrimType value_or(PrimType PT) const {
89 if (operator bool())
90 return static_cast<PrimType>(V);
91 return PT;
92 }
93
94 bool operator==(PrimType PT) const {
95 if (!operator bool())
96 return false;
97 return V == static_cast<unsigned>(PT);
98 }
99 bool operator==(OptPrimType OPT) const { return V == OPT.V; }
100 bool operator!=(PrimType PT) const { return !(*this == PT); }
101 bool operator!=(OptPrimType OPT) const { return V != OPT.V; }
102};
103static_assert(sizeof(OptPrimType) == sizeof(PrimType));
104
105enum class CastKind : uint8_t {
106 Reinterpret,
107 ReinterpretLike,
108 ReinterpretPtrToInt,
109 Volatile,
110 Dynamic,
111};
112
113inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
114 interp::CastKind CK) {
115 switch (CK) {
116 case interp::CastKind::Reinterpret:
117 case interp::CastKind::ReinterpretPtrToInt:
118 OS << "reinterpret_cast";
119 break;
120 case interp::CastKind::ReinterpretLike:
121 OS << "reinterpret_like";
122 break;
123 case interp::CastKind::Volatile:
124 OS << "volatile";
125 break;
126 case interp::CastKind::Dynamic:
127 OS << "dynamic";
128 break;
129 }
130 return OS;
131}
132
133template <typename T> constexpr bool needsAlloc() {
134 return std::is_same_v<T, IntegralAP<false>> ||
135 std::is_same_v<T, IntegralAP<true>> || std::is_same_v<T, Floating> ||
136 std::is_same_v<T, MemberPointer>;
137}
138constexpr bool needsAlloc(PrimType T) {
139 return T == PT_IntAP || T == PT_IntAPS || T == PT_Float || T == PT_MemberPtr;
140}
141
142template <typename T> constexpr bool isIntegralOrPointer() {
143 return std::is_same_v<T, Integral<16, false>> ||
144 std::is_same_v<T, Integral<16, true>> ||
145 std::is_same_v<T, Integral<32, false>> ||
146 std::is_same_v<T, Integral<32, true>> ||
147 std::is_same_v<T, Integral<64, false>> ||
148 std::is_same_v<T, Integral<64, true>>;
149}
150
151template <typename T> constexpr bool isFixedSizeIntegralType() {
152 return std::is_same_v<T, Char<false>> || std::is_same_v<T, Char<true>> ||
153 std::is_same_v<T, Integral<16, false>> ||
154 std::is_same_v<T, Integral<16, true>> ||
155 std::is_same_v<T, Integral<32, false>> ||
156 std::is_same_v<T, Integral<32, true>> ||
157 std::is_same_v<T, Integral<64, false>> ||
158 std::is_same_v<T, Integral<64, true>>;
159}
160
161/// Mapping from primitive types to their representation.
162template <PrimType T> struct PrimConv;
163template <> struct PrimConv<PT_Sint8> {
164 using T = Char<true>;
165};
166template <> struct PrimConv<PT_Uint8> {
167 using T = Char<false>;
168};
169template <> struct PrimConv<PT_Sint16> {
170 using T = Integral<16, true>;
171};
172template <> struct PrimConv<PT_Uint16> {
173 using T = Integral<16, false>;
174};
175template <> struct PrimConv<PT_Sint32> {
176 using T = Integral<32, true>;
177};
178template <> struct PrimConv<PT_Uint32> {
179 using T = Integral<32, false>;
180};
181template <> struct PrimConv<PT_Sint64> {
182 using T = Integral<64, true>;
183};
184template <> struct PrimConv<PT_Uint64> {
185 using T = Integral<64, false>;
186};
187template <> struct PrimConv<PT_IntAP> {
188 using T = IntegralAP<false>;
189};
190template <> struct PrimConv<PT_IntAPS> {
191 using T = IntegralAP<true>;
192};
193template <> struct PrimConv<PT_Float> {
194 using T = Floating;
195};
196template <> struct PrimConv<PT_Bool> {
197 using T = Boolean;
198};
199template <> struct PrimConv<PT_Ptr> {
200 using T = Pointer;
201};
202template <> struct PrimConv<PT_MemberPtr> {
203 using T = MemberPointer;
204};
205template <> struct PrimConv<PT_FixedPoint> {
206 using T = FixedPoint;
207};
208
209/// Returns the size of a primitive type in bytes.
210size_t primSize(PrimType Type);
211
212/// Aligns a size to the pointer alignment.
213constexpr size_t align(size_t Size) {
214 return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *);
215}
216
217constexpr bool aligned(uintptr_t Value) { return Value == align(Size: Value); }
218static_assert(aligned(Value: sizeof(void *)));
219
220static inline bool aligned(const void *P) {
221 return aligned(Value: reinterpret_cast<uintptr_t>(P));
222}
223
224} // namespace interp
225} // namespace clang
226
227/// Helper macro to simplify type switches.
228/// The macro implicitly exposes a type T in the scope of the inner block.
229#define TYPE_SWITCH_CASE(Name, B) \
230 case Name: { \
231 using T = PrimConv<Name>::T; \
232 B; \
233 break; \
234 }
235#define TYPE_SWITCH(Expr, B) \
236 do { \
237 switch (Expr) { \
238 TYPE_SWITCH_CASE(PT_Sint8, B) \
239 TYPE_SWITCH_CASE(PT_Uint8, B) \
240 TYPE_SWITCH_CASE(PT_Sint16, B) \
241 TYPE_SWITCH_CASE(PT_Uint16, B) \
242 TYPE_SWITCH_CASE(PT_Sint32, B) \
243 TYPE_SWITCH_CASE(PT_Uint32, B) \
244 TYPE_SWITCH_CASE(PT_Sint64, B) \
245 TYPE_SWITCH_CASE(PT_Uint64, B) \
246 TYPE_SWITCH_CASE(PT_IntAP, B) \
247 TYPE_SWITCH_CASE(PT_IntAPS, B) \
248 TYPE_SWITCH_CASE(PT_Float, B) \
249 TYPE_SWITCH_CASE(PT_Bool, B) \
250 TYPE_SWITCH_CASE(PT_Ptr, B) \
251 TYPE_SWITCH_CASE(PT_MemberPtr, B) \
252 TYPE_SWITCH_CASE(PT_FixedPoint, B) \
253 } \
254 } while (0)
255
256#define INT_TYPE_SWITCH(Expr, B) \
257 do { \
258 switch (Expr) { \
259 TYPE_SWITCH_CASE(PT_Sint8, B) \
260 TYPE_SWITCH_CASE(PT_Uint8, B) \
261 TYPE_SWITCH_CASE(PT_Sint16, B) \
262 TYPE_SWITCH_CASE(PT_Uint16, B) \
263 TYPE_SWITCH_CASE(PT_Sint32, B) \
264 TYPE_SWITCH_CASE(PT_Uint32, B) \
265 TYPE_SWITCH_CASE(PT_Sint64, B) \
266 TYPE_SWITCH_CASE(PT_Uint64, B) \
267 TYPE_SWITCH_CASE(PT_IntAP, B) \
268 TYPE_SWITCH_CASE(PT_IntAPS, B) \
269 TYPE_SWITCH_CASE(PT_Bool, B) \
270 default: \
271 llvm_unreachable("Not an integer value"); \
272 } \
273 } while (0)
274
275#define FIXED_SIZE_INT_TYPE_SWITCH(Expr, B) \
276 do { \
277 switch (Expr) { \
278 TYPE_SWITCH_CASE(PT_Sint8, B) \
279 TYPE_SWITCH_CASE(PT_Uint8, B) \
280 TYPE_SWITCH_CASE(PT_Sint16, B) \
281 TYPE_SWITCH_CASE(PT_Uint16, B) \
282 TYPE_SWITCH_CASE(PT_Sint32, B) \
283 TYPE_SWITCH_CASE(PT_Uint32, B) \
284 TYPE_SWITCH_CASE(PT_Sint64, B) \
285 TYPE_SWITCH_CASE(PT_Uint64, B) \
286 default: \
287 llvm_unreachable("Not an integer value"); \
288 } \
289 } while (0)
290
291#define INT_TYPE_SWITCH_NO_BOOL(Expr, B) \
292 do { \
293 switch (Expr) { \
294 TYPE_SWITCH_CASE(PT_Sint8, B) \
295 TYPE_SWITCH_CASE(PT_Uint8, B) \
296 TYPE_SWITCH_CASE(PT_Sint16, B) \
297 TYPE_SWITCH_CASE(PT_Uint16, B) \
298 TYPE_SWITCH_CASE(PT_Sint32, B) \
299 TYPE_SWITCH_CASE(PT_Uint32, B) \
300 TYPE_SWITCH_CASE(PT_Sint64, B) \
301 TYPE_SWITCH_CASE(PT_Uint64, B) \
302 TYPE_SWITCH_CASE(PT_IntAP, B) \
303 TYPE_SWITCH_CASE(PT_IntAPS, B) \
304 default: \
305 llvm_unreachable("Not an integer value"); \
306 } \
307 } while (0)
308
309#define TYPE_SWITCH_ALLOC(Expr, B) \
310 do { \
311 switch (Expr) { \
312 TYPE_SWITCH_CASE(PT_Float, B) \
313 TYPE_SWITCH_CASE(PT_IntAP, B) \
314 TYPE_SWITCH_CASE(PT_IntAPS, B) \
315 TYPE_SWITCH_CASE(PT_MemberPtr, B) \
316 default:; \
317 } \
318 } while (0)
319
320#endif
321