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