OpenVDB  11.0.0
Types.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file codegen/Types.h
5 ///
6 /// @authors Nick Avramoussis
7 ///
8 /// @brief Consolidated llvm types for most supported types
9 ///
10 
11 #ifndef OPENVDB_AX_CODEGEN_TYPES_HAS_BEEN_INCLUDED
12 #define OPENVDB_AX_CODEGEN_TYPES_HAS_BEEN_INCLUDED
13 
14 #include "openvdb_ax/ast/Tokens.h"
15 #include "openvdb_ax/Exceptions.h"
16 #include "String.h"
17 
18 #include <openvdb/version.h>
19 #include <openvdb/Types.h>
20 #include <openvdb/math/Mat3.h>
21 #include <openvdb/math/Mat4.h>
22 #include <openvdb/math/Vec3.h>
23 
24 #include <llvm/IR/Constants.h>
25 #include <llvm/IR/IRBuilder.h>
26 #include <llvm/IR/LLVMContext.h>
27 
28 #include <type_traits>
29 
30 namespace openvdb {
32 namespace OPENVDB_VERSION_NAME {
33 
34 namespace ax {
35 namespace codegen {
36 
37 template <size_t Bits> struct int_t;
38 template <> struct int_t<8> { using type = int8_t; };
39 template <> struct int_t<16> { using type = int16_t; };
40 template <> struct int_t<32> { using type = int32_t; };
41 template <> struct int_t<64> { using type = int64_t; };
42 
43 /// @brief LLVM type mapping from pod types
44 /// @note LLVM Types do not store information about the value sign, only meta
45 /// information about the primitive type (i.e. float, int, pointer) and
46 /// the precision width. LLVMType<uint64_t>::get(C) will provide the same
47 /// type as LLVMType<int64_t>::get(C), however sign is taken into account
48 /// during construction of LLVM constants.
49 /// @note LLVMType classes are importantly used to provided automatic external
50 /// function mapping. Note that references are not supported, pointers
51 /// should be used instead.
52 /// @note Provide your own custom class mapping by specializing the below.
53 template <typename T>
54 struct LLVMType
55 {
56  static_assert(!std::is_reference<T>::value,
57  "Reference types/arguments are not supported for automatic "
58  "LLVM Type conversion. Use pointers instead.");
59  static_assert(!std::is_class<T>::value,
60  "Object types/arguments are not supported for automatic "
61  "LLVM Type conversion.");
62 
63  /// @brief Return an LLVM type which represents T
64  /// @param C The LLVMContext to request the Type from.
65  static inline llvm::Type*
66  get(llvm::LLVMContext& C)
67  {
68  // @note bools always treated as i1 values as the constants
69  // true and false from the IRBuilder are i1
70  if (std::is_same<T, bool>::value) {
71  return llvm::Type::getInt1Ty(C);
72  }
73 
74 #if LLVM_VERSION_MAJOR > 6
75  return llvm::Type::getScalarTy<T>(C);
76 #else
77  int bits = sizeof(T) * CHAR_BIT;
78  if (std::is_integral<T>::value) {
79  return llvm::Type::getIntNTy(C, bits);
80  }
81  else if (std::is_floating_point<T>::value) {
82  switch (bits) {
83  case 16: return llvm::Type::getHalfTy(C);
84  case 32: return llvm::Type::getFloatTy(C);
85  case 64: return llvm::Type::getDoubleTy(C);
86  }
87  }
88  OPENVDB_THROW(AXCodeGenError, "LLVMType called with an unsupported type \"" +
89  std::string(typeNameAsString<T>()) + "\".");
90 #endif
91  }
92 
93  /// @brief Return an LLVM constant Value which represents T value
94  /// @param C The LLVMContext
95  /// @param V The value to convert to an LLVM constant
96  /// @return If successful, returns a pointer to an LLVM constant which
97  /// holds the value T.
98  static inline llvm::Constant*
99  get(llvm::LLVMContext& C, const T V)
100  {
101  llvm::Type* type = LLVMType<T>::get(C);
102  llvm::Constant* constant = nullptr;
103 
104  if (std::is_floating_point<T>::value) {
105  assert(llvm::ConstantFP::isValueValidForType(type,
106  llvm::APFloat(static_cast<typename std::conditional
107  <std::is_floating_point<T>::value, T, double>::type>(V))));
108  constant = llvm::ConstantFP::get(type, static_cast<double>(V));
109  }
110  else if (std::is_integral<T>::value) {
111  const constexpr bool isSigned = std::is_signed<T>::value;
112  assert((isSigned && llvm::ConstantInt::isValueValidForType(type, static_cast<int64_t>(V))) ||
113  (!isSigned && llvm::ConstantInt::isValueValidForType(type, static_cast<uint64_t>(V))));
114  constant = llvm::ConstantInt::get(type, static_cast<uint64_t>(V), isSigned);
115  }
116 
117  assert(constant);
118  return constant;
119  }
120 
121  /// @brief Return an LLVM constant which holds an uintptr_t, representing
122  /// the current address of the given value.
123  /// @param C The LLVMContext
124  /// @param V The address of a given type to convert to an LLVM constant
125  static inline llvm::Constant*
126  get(llvm::LLVMContext& C, const T* const V)
127  {
128  return LLVMType<uintptr_t>::get(C,
129  reinterpret_cast<uintptr_t>(V));
130  }
131 };
132 
133 template <typename T, size_t S>
134 struct LLVMType<T[S]>
135 {
136  static_assert(S != 0,
137  "Zero size array types are not supported for automatic LLVM "
138  "Type conversion");
139 
140  static inline llvm::Type*
141  get(llvm::LLVMContext& C) {
142  return llvm::ArrayType::get(LLVMType<T>::get(C), S);
143  }
144  static inline llvm::Constant*
145  get(llvm::LLVMContext& C, const T(&array)[S]) {
146  return llvm::ConstantDataArray::get(C, array);
147  }
148  static inline llvm::Constant*
149  get(llvm::LLVMContext& C, const T(*array)[S])
150  {
151  return LLVMType<uintptr_t>::get(C,
152  reinterpret_cast<uintptr_t>(array));
153  }
154 };
155 
156 template <typename T>
157 struct LLVMType<T*>
158 {
159  static inline llvm::PointerType*
160  get(llvm::LLVMContext& C) {
161  return LLVMType<T>::get(C)->getPointerTo(0);
162  }
163 };
164 
165 template <>
166 struct LLVMType<char> : public LLVMType<uint8_t>
167 {
168  static_assert(std::is_same<uint8_t, unsigned char>::value,
169  "This library requires std::uint8_t to be implemented as unsigned char.");
170 };
171 
172 template <>
173 struct LLVMType<codegen::String>
174 {
175  static inline llvm::StructType*
176  get(llvm::LLVMContext& C) {
177  const std::vector<llvm::Type*> types {
178  LLVMType<char*>::get(C), // ptr
180  LLVMType<int64_t>::get(C) // size
181  };
182  return llvm::StructType::get(C, types);
183  }
184  static inline llvm::Constant*
185  get(llvm::LLVMContext& C, const codegen::String* const string)
186  {
187  return LLVMType<uintptr_t>::get(C,
188  reinterpret_cast<uintptr_t>(string));
189  }
190 };
191 
192 template <>
193 struct LLVMType<void>
194 {
195  static inline llvm::Type*
196  get(llvm::LLVMContext& C) {
197  return llvm::Type::getVoidTy(C);
198  }
199 };
200 
201 /// @note void* implemented as signed int8_t* to match clang IR generation
202 template <> struct LLVMType<void*> : public LLVMType<int8_t*> {};
203 template <> struct LLVMType<openvdb::math::half>
204 {
205  // @note LLVM has a special representation of half types. Don't alias to
206  // uint16_t as we want type->isFloatingPointTy() to still return true.
207 
208  static inline llvm::Type* get(llvm::LLVMContext& C) { return llvm::Type::getHalfTy(C); }
209  static inline llvm::Constant* get(llvm::LLVMContext& C, const openvdb::math::half V)
210  {
211  llvm::Type* type = LLVMType<openvdb::math::half>::get(C);
212  assert(llvm::ConstantFP::isValueValidForType(type, llvm::APFloat(V)));
213  llvm::Constant* constant = llvm::ConstantFP::get(type, static_cast<double>(V));
214  assert(constant);
215  return constant;
216  }
217  static inline llvm::Constant* get(llvm::LLVMContext& C, const openvdb::math::half* const V)
218  {
219  return LLVMType<uintptr_t>::get(C, reinterpret_cast<uintptr_t>(V));
220  }
221 };
222 
223 template <typename T> struct LLVMType<const T> : public LLVMType<T> {};
224 template <typename T> struct LLVMType<const T*> : public LLVMType<T*> {};
225 
226 /// @brief Alias mapping between two types, a frontend type T1 and a backend
227 /// type T2. This class is the intended interface for binding objects
228 /// which implement supported backend AX/IR types to this given backend
229 /// type. More specifically, it's current and expected usage is limited
230 /// to objects which hold a single member of a supported backend type
231 /// and implements a StandardLayoutType as defined by the standard.
232 /// Fundamentally, T1->T2 mapping should be supported by
233 /// reinterpret_cast<> as defined by the type aliasing rules.
234 /// @note The static asserts provide preliminary checks but are by no means
235 /// a guarantee that a provided mapping is correct. Ensure the above
236 /// requirements are met when instantiating an alias.
237 template <typename T1, typename T2>
239 {
241 
242  static_assert(sizeof(T1) == sizeof(T2),
243  "T1 differs in size to T2 during alias mapping. Types should have "
244  "the same memory layout.");
245  static_assert(std::is_standard_layout<T1>::value,
246  "T1 in instantiation of an AliasTypeMap does not have a standard layout. "
247  "This will most likely cause undefined behaviour when attempting to map "
248  "T1->T2.");
249 
250  static inline llvm::Type*
251  get(llvm::LLVMContext& C) {
252  return LLVMTypeT::get(C);
253  }
254  static inline llvm::Constant*
255  get(llvm::LLVMContext& C, const T1& value) {
256  return LLVMTypeT::get(C, reinterpret_cast<const T2&>(value));
257  }
258  static inline llvm::Constant*
259  get(llvm::LLVMContext& C, const T1* const value) {
260  return LLVMTypeT::get(C, reinterpret_cast<const T2* const>(value));
261  }
262 };
263 
264 /// @brief Supported aliasing for VDB math types, allowing use in external
265 /// function signatures.
266 template <typename T> struct LLVMType<openvdb::math::Vec2<T>> : public AliasTypeMap<openvdb::math::Vec2<T>, T[2]> {};
267 template <typename T> struct LLVMType<openvdb::math::Vec3<T>> : public AliasTypeMap<openvdb::math::Vec3<T>, T[3]> {};
268 template <typename T> struct LLVMType<openvdb::math::Vec4<T>> : public AliasTypeMap<openvdb::math::Vec4<T>, T[4]> {};
269 template <typename T> struct LLVMType<openvdb::math::Mat3<T>> : public AliasTypeMap<openvdb::math::Mat3<T>, T[9]> {};
270 template <typename T> struct LLVMType<openvdb::math::Mat4<T>> : public AliasTypeMap<openvdb::math::Mat4<T>, T[16]> {};
271 
272 ///////////////////////////////////////////////////////////////////////////
273 ///////////////////////////////////////////////////////////////////////////
274 
275 /// @brief Templated function traits which provides compile-time index access to
276 /// the types of the function signature
277 ///
278 template<typename SignatureT>
280 
281 template<typename R, typename... Args>
282 struct FunctionTraits<R(&)(Args...)> : public FunctionTraits<R(Args...)> {};
283 
284 template<typename R, typename... Args>
285 struct FunctionTraits<R(*)(Args...)> : public FunctionTraits<R(Args...)> {};
286 
287 // Only enable noexcept signatures from C++17 onwards when it is actually
288 // respected. Otherwise the compiler ignores it and we get duplicating
289 // definitions for FunctionTraits specializations.
290 #if __cplusplus >= 201703L
291 template<typename R, typename... Args>
292 struct FunctionTraits<R(Args...) noexcept> : public FunctionTraits<R(Args...)> {};
293 
294 template<typename R, typename... Args>
295 struct FunctionTraits<R(*)(Args...) noexcept> : public FunctionTraits<R(Args...)> {};
296 #endif
297 
298 template<typename ReturnT, typename ...Args>
299 struct FunctionTraits<ReturnT(Args...)>
300 {
301  using ReturnType = ReturnT;
302  using SignatureType = ReturnType(Args...);
303  static const size_t N_ARGS = sizeof...(Args);
304 
305  template <size_t I>
306  struct Arg
307  {
308  public:
309  static_assert(I < N_ARGS,
310  "Invalid index specified for function argument access");
311  using Type = typename std::tuple_element<I, std::tuple<Args...>>::type;
312  static_assert(!std::is_reference<Type>::value,
313  "Reference types/arguments are not supported for automatic "
314  "LLVM Type conversion. Use pointers instead.");
315  };
316 };
317 
318 ///////////////////////////////////////////////////////////////////////////
319 ///////////////////////////////////////////////////////////////////////////
320 
321 /// @brief Returns an llvm Constant holding a scalar value
322 /// @param t The scalar constant
323 /// @param type The LLVM type. Can differ from the type of t, in which
324 /// case the value will be cast to the llvm type
325 ///
326 template <typename T>
327 inline llvm::Constant*
328 llvmConstant(const T t, llvm::Type* type)
329 {
330  static_assert(std::is_floating_point<T>::value || std::is_integral<T>::value,
331  "T type for llvmConstant must be a floating point or integral type.");
332 
333  if (type->isIntegerTy()) {
334  return llvm::ConstantInt::get(type, static_cast<uint64_t>(t), /*signed*/true);
335  }
336  else {
337  assert(type->isFloatingPointTy());
338  return llvm::ConstantFP::get(type, static_cast<double>(t));
339  }
340 }
341 
342 /// @brief Returns an llvm IntegerType given a requested size and context
343 /// @param size The number of bits of the integer type
344 /// @param C The LLVMContext to request the Type from.
345 ///
346 OPENVDB_AX_API llvm::IntegerType* llvmIntType(const uint32_t size, llvm::LLVMContext& C);
347 
348 /// @brief Returns an llvm floating point Type given a requested size and context
349 /// @param size The size of the float to request, i.e. float - 32, double - 64 etc.
350 /// @param C The LLVMContext to request the Type from.
351 ///
352 OPENVDB_AX_API llvm::Type* llvmFloatType(const uint32_t size, llvm::LLVMContext& C);
353 
354 /// @brief Returns an llvm type representing a type defined by a string.
355 /// @note For string types, this function returns the element type, not the
356 /// object type! The llvm type representing a char block of memory
357 /// is LLVMType<char*>::get(C);
358 /// @param type The AX token type
359 /// @param C The LLVMContext to request the Type from.
360 ///
361 OPENVDB_AX_API llvm::Type* llvmTypeFromToken(const ast::tokens::CoreType& type, llvm::LLVMContext& C);
362 
363 /// @brief Return a corresponding AX token which represents the given LLVM Type.
364 /// @note If the type does not exist in AX, ast::tokens::UNKNOWN is returned.
365 /// Must not be a nullptr.
366 /// @param type a valid LLVM Type
367 ///
369 
370 } // namespace codegen
371 } // namespace ax
372 } // namespace OPENVDB_VERSION_NAME
373 } // namespace openvdb
374 
375 #endif // OPENVDB_AX_CODEGEN_TYPES_HAS_BEEN_INCLUDED
376 
Definition: Exceptions.h:38
LLVM type mapping from pod types.
Definition: Types.h:54
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
int8_t type
Definition: Types.h:38
typename std::tuple_element< I, std::tuple< Args... >>::type Type
Definition: Types.h:311
CoreType
Definition: Tokens.h:31
#define OPENVDB_AX_API
Definition: Platform.h:295
internal::half half
Definition: Types.h:29
OPENVDB_AX_API llvm::Type * llvmFloatType(const uint32_t size, llvm::LLVMContext &C)
Returns an llvm floating point Type given a requested size and context.
Definition: Mat.h:165
int16_t type
Definition: Types.h:39
ReturnType(Args...) SignatureType
Definition: Types.h:302
Definition: Vec2.h:23
Definition: Mat4.h:24
Definition: Exceptions.h:13
int32_t type
Definition: Types.h:40
OpenVDB AX Exceptions.
An extremely basic but native representation of a string class with SSO support. This exists to provi...
Definition: String.h:33
Alias mapping between two types, a frontend type T1 and a backend type T2. This class is the intended...
Definition: Types.h:238
llvm::Constant * llvmConstant(const T t, llvm::Type *type)
Returns an llvm Constant holding a scalar value.
Definition: Types.h:328
3x3 matrix class.
Definition: Mat3.h:28
Various function and operator tokens used throughout the AST and code generation. ...
Templated function traits which provides compile-time index access to the types of the function signa...
Definition: Types.h:279
OPENVDB_AX_API llvm::Type * llvmTypeFromToken(const ast::tokens::CoreType &type, llvm::LLVMContext &C)
Returns an llvm type representing a type defined by a string.
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
int64_t type
Definition: Types.h:41
OPENVDB_AX_API ast::tokens::CoreType tokenFromLLVMType(const llvm::Type *type)
Return a corresponding AX token which represents the given LLVM Type.
Provides the class definition for the equivalent IR representation and logic for strings in AX...
4x4 -matrix class.
Definition: Mat3.h:22
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:212
OPENVDB_AX_API llvm::IntegerType * llvmIntType(const uint32_t size, llvm::LLVMContext &C)
Returns an llvm IntegerType given a requested size and context.