OpenVDB  9.0.1
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
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;
79  return llvm::Type::getIntNTy(C, bits);
80  }
82  switch (bits) {
83  case 32: return llvm::Type::getFloatTy(C);
84  case 64: return llvm::Type::getDoubleTy(C);
85  }
86  }
87  OPENVDB_THROW(AXCodeGenError, "LLVMType called with an unsupported type \"" +
88  std::string(typeNameAsString<T>()) + "\".");
89 #endif
90  }
91 
92  /// @brief Return an LLVM constant Value which represents T value
93  /// @param C The LLVMContext
94  /// @param V The value to convert to an LLVM constant
95  /// @return If successful, returns a pointer to an LLVM constant which
96  /// holds the value T.
97  static inline llvm::Constant*
98  get(llvm::LLVMContext& C, const T V)
99  {
100  llvm::Type* type = LLVMType<T>::get(C);
101  llvm::Constant* constant = nullptr;
102 
104  assert(llvm::ConstantFP::isValueValidForType(type,
105  llvm::APFloat(static_cast<typename std::conditional
106  <std::is_floating_point<T>::value, T, double>::type>(V))));
107  constant = llvm::ConstantFP::get(type, static_cast<double>(V));
108  }
109  else if (std::is_integral<T>::value) {
110  const constexpr bool isSigned = std::is_signed<T>::value;
111  assert((isSigned && llvm::ConstantInt::isValueValidForType(type, static_cast<int64_t>(V))) ||
112  (!isSigned && llvm::ConstantInt::isValueValidForType(type, static_cast<uint64_t>(V))));
113  constant = llvm::ConstantInt::get(type, static_cast<uint64_t>(V), isSigned);
114  }
115 
116  assert(constant);
117  return constant;
118  }
119 
120  /// @brief Return an LLVM constant which holds an uintptr_t, representing
121  /// the current address of the given value.
122  /// @param C The LLVMContext
123  /// @param V The address of a given type to convert to an LLVM constant
124  static inline llvm::Constant*
125  get(llvm::LLVMContext& C, const T* const V)
126  {
127  return LLVMType<uintptr_t>::get(C,
128  reinterpret_cast<uintptr_t>(V));
129  }
130 };
131 
132 template <typename T, size_t S>
133 struct LLVMType<T[S]>
134 {
135  static_assert(S != 0,
136  "Zero size array types are not supported for automatic LLVM "
137  "Type conversion");
138 
139  static inline llvm::Type*
140  get(llvm::LLVMContext& C) {
141  return llvm::ArrayType::get(LLVMType<T>::get(C), S);
142  }
143  static inline llvm::Constant*
144  get(llvm::LLVMContext& C, const T(&array)[S]) {
145  return llvm::ConstantDataArray::get(C, array);
146  }
147  static inline llvm::Constant*
148  get(llvm::LLVMContext& C, const T(*array)[S])
149  {
150  return LLVMType<uintptr_t>::get(C,
151  reinterpret_cast<uintptr_t>(array));
152  }
153 };
154 
155 template <typename T>
156 struct LLVMType<T*>
157 {
158  static inline llvm::PointerType*
159  get(llvm::LLVMContext& C) {
160  return LLVMType<T>::get(C)->getPointerTo(0);
161  }
162 };
163 
164 template <>
165 struct LLVMType<char> : public LLVMType<uint8_t>
166 {
168  "This library requires std::uint8_t to be implemented as unsigned char.");
169 };
170 
171 template <>
172 struct LLVMType<codegen::String>
173 {
174  static inline llvm::StructType*
175  get(llvm::LLVMContext& C) {
176  const std::vector<llvm::Type*> types {
177  LLVMType<char*>::get(C), // ptr
179  LLVMType<int64_t>::get(C) // size
180  };
181  return llvm::StructType::get(C, types);
182  }
183  static inline llvm::Constant*
184  get(llvm::LLVMContext& C, const codegen::String* const string)
185  {
186  return LLVMType<uintptr_t>::get(C,
187  reinterpret_cast<uintptr_t>(string));
188  }
189 };
190 
191 template <>
192 struct LLVMType<void>
193 {
194  static inline llvm::Type*
195  get(llvm::LLVMContext& C) {
196  return llvm::Type::getVoidTy(C);
197  }
198 };
199 
200 /// @note void* implemented as signed int_t* to match clang IR generation
201 template <> struct LLVMType<void*> : public LLVMType<int_t<sizeof(void*)>::type*> {};
202 
203 template <typename T> struct LLVMType<const T> : public LLVMType<T> {};
204 template <typename T> struct LLVMType<const T*> : public LLVMType<T*> {};
205 
206 /// @brief Alias mapping between two types, a frontend type T1 and a backend
207 /// type T2. This class is the intended interface for binding objects
208 /// which implement supported backend AX/IR types to this given backend
209 /// type. More specifically, it's current and expected usage is limited
210 /// to objects which hold a single member of a supported backend type
211 /// and implements a StandardLayoutType as defined by the standard.
212 /// Fundamentally, T1->T2 mapping should be supported by
213 /// reinterpret_cast<> as defined by the type aliasing rules.
214 /// @note The static asserts provide preliminary checks but are by no means
215 /// a guarantee that a provided mapping is correct. Ensure the above
216 /// requirements are met when instantiating an alias.
217 template <typename T1, typename T2>
219 {
221 
222  static_assert(sizeof(T1) == sizeof(T2),
223  "T1 differs in size to T2 during alias mapping. Types should have "
224  "the same memory layout.");
226  "T1 in instantiation of an AliasTypeMap does not have a standard layout. "
227  "This will most likely cause undefined behaviour when attempting to map "
228  "T1->T2.");
229 
230  static inline llvm::Type*
231  get(llvm::LLVMContext& C) {
232  return LLVMTypeT::get(C);
233  }
234  static inline llvm::Constant*
235  get(llvm::LLVMContext& C, const T1& value) {
236  return LLVMTypeT::get(C, reinterpret_cast<const T2&>(value));
237  }
238  static inline llvm::Constant*
239  get(llvm::LLVMContext& C, const T1* const value) {
240  return LLVMTypeT::get(C, reinterpret_cast<const T2* const>(value));
241  }
242 };
243 
244 /// @brief Supported aliasing for VDB math types, allowing use in external
245 /// function signatures.
246 template <typename T> struct LLVMType<math::Vec2<T>> : public AliasTypeMap<math::Vec2<T>, T[2]> {};
247 template <typename T> struct LLVMType<math::Vec3<T>> : public AliasTypeMap<math::Vec3<T>, T[3]> {};
248 template <typename T> struct LLVMType<math::Vec4<T>> : public AliasTypeMap<math::Vec4<T>, T[4]> {};
249 template <typename T> struct LLVMType<math::Mat3<T>> : public AliasTypeMap<math::Mat3<T>, T[9]> {};
250 template <typename T> struct LLVMType<math::Mat4<T>> : public AliasTypeMap<math::Mat4<T>, T[16]> {};
251 
252 ///////////////////////////////////////////////////////////////////////////
253 ///////////////////////////////////////////////////////////////////////////
254 
255 /// @brief Templated function traits which provides compile-time index access to
256 /// the types of the function signature
257 ///
258 template<typename SignatureT>
260 
261 template<typename R, typename... Args>
262 struct FunctionTraits<R(&)(Args...)> : public FunctionTraits<R(Args...)> {};
263 
264 template<typename R, typename... Args>
265 struct FunctionTraits<R(*)(Args...)> : public FunctionTraits<R(Args...)> {};
266 
267 // Only enable noexcept signatures from C++17 onwards when it is actually
268 // respected. Otherwise the compiler ignores it and we get duplicating
269 // definitions for FunctionTraits specializations.
270 #if __cplusplus >= 201703L
271 template<typename R, typename... Args>
272 struct FunctionTraits<R(Args...) noexcept> : public FunctionTraits<R(Args...)> {};
273 
274 template<typename R, typename... Args>
275 struct FunctionTraits<R(*)(Args...) noexcept> : public FunctionTraits<R(Args...)> {};
276 #endif
277 
278 template<typename ReturnT, typename ...Args>
279 struct FunctionTraits<ReturnT(Args...)>
280 {
281  using ReturnType = ReturnT;
282  using SignatureType = ReturnType(Args...);
283  static const size_t N_ARGS = sizeof...(Args);
284 
285  template <size_t I>
286  struct Arg
287  {
288  public:
289  static_assert(I < N_ARGS,
290  "Invalid index specified for function argument access");
291  using Type = typename std::tuple_element<I, std::tuple<Args...>>::type;
292  static_assert(!std::is_reference<Type>::value,
293  "Reference types/arguments are not supported for automatic "
294  "LLVM Type conversion. Use pointers instead.");
295  };
296 };
297 
298 ///////////////////////////////////////////////////////////////////////////
299 ///////////////////////////////////////////////////////////////////////////
300 
301 /// @brief Returns an llvm Constant holding a scalar value
302 /// @param t The scalar constant
303 /// @param type The LLVM type. Can differ from the type of t, in which
304 /// case the value will be cast to the llvm type
305 ///
306 template <typename T>
307 inline llvm::Constant*
308 llvmConstant(const T t, llvm::Type* type)
309 {
311  "T type for llvmConstant must be a floating point or integral type.");
312 
313  if (type->isIntegerTy()) {
314  return llvm::ConstantInt::get(type, static_cast<uint64_t>(t), /*signed*/true);
315  }
316  else {
317  assert(type->isFloatingPointTy());
318  return llvm::ConstantFP::get(type, static_cast<double>(t));
319  }
320 }
321 
322 /// @brief Returns an llvm IntegerType given a requested size and context
323 /// @param size The number of bits of the integer type
324 /// @param C The LLVMContext to request the Type from.
325 ///
326 llvm::IntegerType* llvmIntType(const uint32_t size, llvm::LLVMContext& C);
327 
328 /// @brief Returns an llvm floating point Type given a requested size and context
329 /// @param size The size of the float to request, i.e. float - 32, double - 64 etc.
330 /// @param C The LLVMContext to request the Type from.
331 ///
332 llvm::Type* llvmFloatType(const uint32_t size, llvm::LLVMContext& C);
333 
334 /// @brief Returns an llvm type representing a type defined by a string.
335 /// @note For string types, this function returns the element type, not the
336 /// object type! The llvm type representing a char block of memory
337 /// is LLVMType<char*>::get(C);
338 /// @param type The AX token type
339 /// @param C The LLVMContext to request the Type from.
340 ///
341 llvm::Type* llvmTypeFromToken(const ast::tokens::CoreType& type, llvm::LLVMContext& C);
342 
343 /// @brief Return a corresponding AX token which represents the given LLVM Type.
344 /// @note If the type does not exist in AX, ast::tokens::UNKNOWN is returned.
345 /// Must not be a nullptr.
346 /// @param type a valid LLVM Type
347 ///
348 ast::tokens::CoreType tokenFromLLVMType(const llvm::Type* type);
349 
350 } // namespace codegen
351 } // namespace ax
352 } // namespace OPENVDB_VERSION_NAME
353 } // namespace openvdb
354 
355 #endif // OPENVDB_AX_CODEGEN_TYPES_HAS_BEEN_INCLUDED
356 
ast::tokens::CoreType tokenFromLLVMType(const llvm::Type *type)
Return a corresponding AX token which represents the given LLVM Type.
llvm::Type * llvmTypeFromToken(const ast::tokens::CoreType &type, llvm::LLVMContext &C)
Returns an llvm type representing a type defined by a string.
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Alias mapping between two types, a frontend type T1 and a backend type T2. This class is the intended...
Definition: Types.h:218
ReturnType(Args...) SignatureType
Definition: Types.h:282
An extremely basic but native representation of a string class with SSO support. This exists to provi...
Definition: String.h:33
llvm::IntegerType * llvmIntType(const uint32_t size, llvm::LLVMContext &C)
Returns an llvm IntegerType given a requested size and context.
int16_t type
Definition: Types.h:39
typename std::tuple_element< I, std::tuple< Args... >>::type Type
Definition: Types.h:291
int32_t type
Definition: Types.h:40
CoreType
Definition: Tokens.h:31
Definition: Types.h:37
int8_t type
Definition: Types.h:38
Templated function traits which provides compile-time index access to the types of the function signa...
Definition: Types.h:259
Definition: Exceptions.h:13
int64_t type
Definition: Types.h:41
ValueT value
Definition: GridBuilder.h:1287
LLVM type mapping from pod types.
Definition: Types.h:54
OpenVDB AX Exceptions.
Definition: Exceptions.h:36
llvm::Type * llvmFloatType(const uint32_t size, llvm::LLVMContext &C)
Returns an llvm floating point Type given a requested size and context.
Various function and operator tokens used throughout the AST and code generation. ...
llvm::Constant * llvmConstant(const T t, llvm::Type *type)
Returns an llvm Constant holding a scalar value.
Definition: Types.h:308
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
Provides the class definition for the equivalent IR representation and logic for strings in AX...
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202