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