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