OpenVDB  13.0.0
FunctionTypes.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 
4 /// @file codegen/FunctionTypes.h
5 ///
6 /// @authors Nick Avramoussis
7 ///
8 /// @brief Contains frameworks for creating custom AX functions which can
9 /// be registered within the FunctionRegistry and used during code
10 /// generation. The intended and safest way to build a function is to
11 /// use the FunctionBuilder struct with its addSignature methods. Note
12 /// that the derived Function classes provided can also be subclassed
13 /// for more granular control, however may be subject to more substantial
14 /// API changes.
15 ///
16 /// @details There are a variety of different ways to build a function
17 /// which are tailored towards different function types. The two currently
18 /// supported function implementations are C Bindings and IR generation.
19 /// Additionally, depending on the return type of the function, you may
20 /// need to declare your function an SRET (structural return) function.
21 ///
22 /// C Bindings:
23 /// As the name suggests, the CFunction class infrastructure provides
24 /// the quickest and easiest way to bind to methods in your host
25 /// application. The most important thing to consider when choosing
26 /// this approach is performance. LLVM will have no knowledge of the
27 /// function body during optimization passes. Depending on the
28 /// implementation of your method and the user's usage from AX, C
29 /// bindings may be subject to limited optimizations in comparison to
30 /// IR functions. For example, a static function which is called from
31 /// within a loop cannot be unrolled. See the CFunction templated
32 /// class.
33 ///
34 /// IR Functions:
35 /// IR Functions expect implementations to generate the body of the
36 /// function directly into IR during code generation. This ensures
37 /// optimal performance during optimization passes however can be
38 /// trickier to design. Note that, in the future, AX functions will
39 /// be internally supported to provide a better solution for
40 /// IR generated functions. See the IRFunction templated class.
41 ///
42 /// SRET Functions:
43 /// Both C Bindings and IR Functions can be marked as SRET methods.
44 /// SRET methods, in AX, are any function which returns a value which
45 /// is not a scalar (e.g. vectors, matrices). This follows the same
46 /// optimization logic as clang which will rebuild function signatures
47 /// with their return type as the first argument if the return type is
48 /// greater than a given size. You should never attempt to return
49 /// alloca's directly from functions (unless malloced).
50 ///
51 /// Some other things to consider:
52 /// - Ensure C Binding dependencies have been correctly mapped.
53 /// - Avoid calling B.CreateAlloca inside of IR functions - instead
54 /// rely on the utility method insertStaticAlloca() where possible.
55 /// - Ensure both floating point and integer argument signatures are
56 /// provided if you wish to avoid floats truncating.
57 /// - Array arguments (vectors/matrices) are always passed by pointer.
58 /// Scalar arguments are always passed by copy.
59 /// - Ensure array arguments which will not be modified are marked as
60 /// readonly. Currently, only array arguments can be passed by
61 /// "reference".
62 /// - Ensure function bodies, return types and parameters and marked
63 /// with desirable llvm attributes.
64 ///
65 
66 #ifndef OPENVDB_AX_CODEGEN_FUNCTION_TYPES_HAS_BEEN_INCLUDED
67 #define OPENVDB_AX_CODEGEN_FUNCTION_TYPES_HAS_BEEN_INCLUDED
68 
69 #include "Types.h"
70 #include "Value.h"
71 #include "Utils.h" // isValidCast
72 #include "ConstantFolding.h"
73 
74 #include <openvdb/version.h>
75 #include <openvdb/util/Assert.h>
76 
77 #include <llvm/ADT/SmallVector.h>
78 #include <llvm/IR/Constants.h>
79 #include <llvm/IR/IRBuilder.h>
80 #include <llvm/IR/Module.h>
81 
82 #include <algorithm>
83 #include <functional>
84 #include <memory>
85 #include <stack>
86 #include <type_traits>
87 #include <map>
88 #include <vector>
89 
90 
91 namespace openvdb {
93 namespace OPENVDB_VERSION_NAME {
94 
95 namespace ax {
96 namespace codegen {
97 
98 struct Function; // fwd
99 
100 ////////////////////////////////////////////////////////////////////////////////
101 ////////////////////////////////////////////////////////////////////////////////
102 
103 /// @brief Typedef a stack allocated array with malloc grow support for
104 /// anything which is relatively small and bases its container size on the
105 /// number of function arguments.
106 /// @note LLVM computes N as 3 (currently) for Value types, but we explicitly
107 /// set this as this is a well-motivates choice for average/median amount of
108 /// function arguments in builtin AX functions.
109 template <typename T>
110 using SmallArgumentVector = llvm::SmallVector<T, 3>;
111 
112 /// @brief Object to array conversion methods to allow functions to return
113 /// vector types. These containers provided an interface for automatic
114 /// conversion of C++ objects to LLVM types as array types.
115 
116 template <typename T, size_t _SIZE = 1>
117 struct ArgType {
118  using Type = T;
119  static const size_t SIZE = _SIZE;
120  using ArrayType = Type[SIZE];
122 };
123 
124 template <typename T, size_t S>
125 struct LLVMType<ArgType<T,S>> : public AliasTypeMap<ArgType<T,S>, T[S]> {};
126 
140 
141 ////////////////////////////////////////////////////////////////////////////////
142 
143 /// @brief Type to symbol conversions - these characters are used to build each
144 /// functions unique signature. They differ from standard AX or LLVM
145 /// syntax to be as short as possible i.e. vec4d, [4 x double] = d4
146 
147 template <typename T> struct TypeToSymbol { static inline std::string s() { return "?"; } };
148 template <> struct TypeToSymbol<void> { static inline std::string s() { return "v"; } };
149 template <> struct TypeToSymbol<char> { static inline std::string s() { return "c"; } };
150 template <> struct TypeToSymbol<uint8_t> { static inline std::string s() { return "u8"; } };
151 template <> struct TypeToSymbol<uint16_t> { static inline std::string s() { return "us"; } };
152 template <> struct TypeToSymbol<uint32_t> { static inline std::string s() { return "ui"; } };
153 template <> struct TypeToSymbol<uint64_t> { static inline std::string s() { return "ul"; } };
154 template <> struct TypeToSymbol<int8_t> { static inline std::string s() { return "8"; } };
155 template <> struct TypeToSymbol<int16_t> { static inline std::string s() { return "s"; } };
156 template <> struct TypeToSymbol<int32_t> { static inline std::string s() { return "i"; } };
157 template <> struct TypeToSymbol<int64_t> { static inline std::string s() { return "l"; } };
158 template <> struct TypeToSymbol<float> { static inline std::string s() { return "f"; } };
159 template <> struct TypeToSymbol<double> { static inline std::string s() { return "d"; } };
160 template <> struct TypeToSymbol<codegen::String> { static inline std::string s() { return "a"; } };
161 
162 template <typename T>
163 struct TypeToSymbol<T*> {
164  static inline std::string s() { return TypeToSymbol<T>::s() + "*"; }
165 };
166 
167 template <typename T, size_t S>
168 struct TypeToSymbol<T[S]> {
169  static inline std::string s() { return TypeToSymbol<T>::s() + std::to_string(S); }
170 };
171 
172 template <typename T, size_t S> struct TypeToSymbol<ArgType<T,S>> : public TypeToSymbol<T[S]> {};
173 template <typename T> struct TypeToSymbol<math::Vec2<T>> : public TypeToSymbol<T[2]> {};
174 template <typename T> struct TypeToSymbol<math::Vec3<T>> : public TypeToSymbol<T[3]> {};
175 template <typename T> struct TypeToSymbol<math::Vec4<T>> : public TypeToSymbol<T[4]> {};
176 template <typename T> struct TypeToSymbol<math::Mat3<T>> : public TypeToSymbol<T[9]> {};
177 template <typename T> struct TypeToSymbol<math::Mat4<T>> : public TypeToSymbol<T[16]> {};
178 template <typename T> struct TypeToSymbol<const T> : public TypeToSymbol<T> {};
179 template <typename T> struct TypeToSymbol<const T*> : public TypeToSymbol<T*> {};
180 
181 ////////////////////////////////////////////////////////////////////////////////
182 ////////////////////////////////////////////////////////////////////////////////
183 
184 /// @brief Templated argument iterator which implements various small functions
185 /// per argument type, resolved at compile time.
186 ///
187 template <typename SignatureT, size_t I = FunctionTraits<SignatureT>::N_ARGS>
189 {
190  using ArgT = typename FunctionTraits<SignatureT>::template Arg<I-1>;
191  using ArgumentValueType = typename ArgT::Type;
192 
193  /// @brief Whether this signature contains types that are representable
194  /// in AX's Value type.
195  static const bool IsNativeSignature =
197  ArgumentIterator<SignatureT, I-1>::IsNativeSignature;
198 
199  template <typename OpT>
200  static void apply(const OpT& op, const bool forwards) {
201  if (forwards) {
204  }
205  else {
208  }
209  }
210 };
211 
212 template <typename SignatureT>
213 struct ArgumentIterator<SignatureT, 0>
214 {
215  static const bool IsNativeSignature = true;
216  template <typename OpT>
217  static void apply(const OpT&, const bool) {}
218 };
219 
220 ////////////////////////////////////////////////////////////////////////////////
221 ////////////////////////////////////////////////////////////////////////////////
222 
223 /// @brief Metadata associated with a function argument or return value.
224 struct ArgInfo
225 {
226  explicit ArgInfo(const Value& val)
227  : ArgInfo(val.GetUnderlyingType(), val.IsPtr() ? 1 : 0) {}
228  explicit ArgInfo(llvm::Type* utype) : ArgInfo(utype, 0) {}
229  ArgInfo(llvm::Type* utype, uint8_t ptrs, bool ret = false)
230  : mUType(utype), mPtrs(ptrs), mReturn(ret) {
231  OPENVDB_ASSERT(mUType);
232  OPENVDB_ASSERT(!mUType->isPointerTy());
233  // void*'s need to be provided as int8_t's
234  OPENVDB_ASSERT(!(mUType->isVoidTy() && mPtrs > 0));
235  }
236  ArgInfo(ArgInfo&&) = default;
237  ArgInfo(const ArgInfo&) = default;
238  ArgInfo& operator=(ArgInfo&&) = default;
239  ArgInfo& operator=(const ArgInfo&) = default;
240  bool operator==(const ArgInfo& other) const
241  {
242  return
243  mUType == other.mUType &&
244  mPtrs == other.mPtrs &&
245  mReturn == other.mReturn;
246  }
247  bool operator!=(const ArgInfo& other) const { return !this->operator==(other); }
248  bool IsMatchingType(const ArgInfo& other) const
249  {
250  return
251  mUType == other.mUType &&
252  mPtrs == other.mPtrs;
253  }
254  bool IsPtr() const { return mPtrs > 0; }
255  uint8_t NumPtrs() const { return mPtrs; }
256  bool IsNative() const { return Value::Supports(mUType) && mPtrs <= 1; }
257  bool IsVoid() const { return mUType->isVoidTy(); }
258  bool IsReturn() const { return mReturn; }
259  llvm::Type* GetUnderlyingType() const { return mUType; }
260  llvm::Type* GetType() const
261  {
262  llvm::Type* type = mUType;
263  for (uint8_t i = 0; i < mPtrs; ++i) {
264  type = llvm::PointerType::get(type, 0);
265  }
266  return type;
267  }
268  void SetIsReturn() { mReturn = true; }
269 private:
270  llvm::Type* mUType; // the underlying argument type
271  uint8_t mPtrs; // num ptrs to the type
272  // true if this is the return argument. For Sret functions, both the
273  // void return and first argument are marked as true.
274  bool mReturn;
275 };
276 
277 /// @brief Container of ArgInfos. This class makes up part of the Function
278 /// API for querying signature information.
280 {
281 public:
283 
284  ArgInfoVector() = default;
285  ArgInfoVector(const std::initializer_list<ArgInfo>& info)
286  : mInfoVec(info) {}
287  ArgInfoVector(ArgInfoVector&&) = default;
288  ArgInfoVector(const ArgInfoVector&) = default;
289  ArgInfoVector& operator=(ArgInfoVector&&) = default;
290  ArgInfoVector& operator=(const ArgInfoVector&) = default;
291 
292  bool operator==(const ArgInfoVector& other) const { return mInfoVec == other.mInfoVec; }
293  bool operator!=(const ArgInfoVector& other) const { return !this->operator==(other); }
294 
295  auto begin() { return mInfoVec.begin(); }
296  auto end() { return mInfoVec.end(); }
297  auto begin() const { return mInfoVec.begin(); }
298  auto end() const { return mInfoVec.end(); }
299  auto rbegin() { return mInfoVec.rbegin(); }
300  auto rend() { return mInfoVec.rend(); }
301  auto rbegin() const { return mInfoVec.rbegin(); }
302  auto rend() const { return mInfoVec.rend(); }
303  auto& front() { return mInfoVec.front(); }
304  auto& front() const { return mInfoVec.front(); }
305  auto& back() { return mInfoVec.back(); }
306  auto& back() const { return mInfoVec.back(); }
307  auto pop_back() { return mInfoVec.pop_back(); }
308  auto clear() { return mInfoVec.clear(); }
309  auto size() const { return mInfoVec.size(); }
310  auto empty() const { return mInfoVec.empty(); }
311  auto erase(ContainerT::const_iterator iter) { return mInfoVec.erase(iter); }
312 
313  void reserve(size_t i) { mInfoVec.reserve(i); }
314  template <typename ...Args>
315  void emplace_back(Args&& ...args) { mInfoVec.emplace_back(std::move(args)...); }
316  ArgInfo& operator[](size_t pos)
317  {
318  OPENVDB_ASSERT(pos < mInfoVec.size());
319  return mInfoVec[pos];
320  }
321  const ArgInfo& operator[](size_t pos) const
322  {
323  OPENVDB_ASSERT(pos < mInfoVec.size());
324  return mInfoVec[pos];
325  }
326 
328  {
330  types.reserve(mInfoVec.size());
331  for (auto& info : mInfoVec) {
332  types.emplace_back(info.GetType());
333  }
334  return types;
335  }
336 
337 private:
338  ContainerT mInfoVec;
339 };
340 
341 /// @brief Wrapper struct to represent "native" function arguments; that is,
342 /// the set of Value type that the AX grammar supports. NativeArguments
343 /// have two benefits; they support casting and implicit function matching
344 /// through FunctionGroups and can be used directly in IR generators (to
345 /// leverage the AX Value API). Functions can still be generated and called
346 /// with "non-native" arguments, but in these cases FunctionGroup::execute
347 /// must result in an explicit signature match
349 {
350  NativeArguments() = default;
351  NativeArguments(const std::initializer_list<Value>& args)
352  : mArgs(args) {}
353  explicit NativeArguments(const std::vector<Value>& args)
354  : mArgs(args.begin(), args.end()) {}
355  NativeArguments(NativeArguments&&) = default;
356  NativeArguments(const NativeArguments&) = default;
357  NativeArguments& operator=(NativeArguments&&) = default;
358  NativeArguments& operator=(const NativeArguments&) = default;
359  size_t size() const { return mArgs.size(); }
360  Value& operator[](size_t pos)
361  {
362  OPENVDB_ASSERT(pos < mArgs.size());
363  return mArgs[pos];
364  }
365  const Value& operator[](size_t pos) const
366  {
367  OPENVDB_ASSERT(pos < mArgs.size());
368  return mArgs[pos];
369  }
370  void AddArg(const Value& val) { mArgs.emplace_back(val); }
371  /// @brief Cast these arguments to match the given function's signature
372  OPENVDB_AX_API NativeArguments Cast(const Function& F, llvm::IRBuilder<>& B) const;
373 private:
375 };
376 
377 /// @brief Arbitrary, potentially "non-native" arguments. This wrapper struct
378 /// can be used when generating function which cannot be called from AX
379 /// itself (e.g. VDB accessor functions or Volume/Point kernels etc). They
380 /// do not support implicit function signature matching or casting.
381 struct Arguments
382 {
383  Arguments() = default;
384 
385  /// @biref Create a set of arguments from native arguments. The function
386  /// framework typically works on generic arguments types.
387  explicit Arguments(const NativeArguments& args) {
388  mArgs.reserve(args.size());
389  mTypes.reserve(args.size());
390  for (size_t i = 0; i < args.size(); ++i) {
391  this->AddArg(args[i]);
392  }
393  }
394 
395  Arguments(Arguments&&) = default;
396  Arguments(const Arguments&) = default;
397  Arguments& operator=(Arguments&&) = default;
398  Arguments& operator=(const Arguments&) = default;
399 
400  size_t size() const { return mArgs.size(); }
401 
402  bool AreNativeValues() const
403  {
404  for (const auto& types : mTypes) {
405  if (!types.IsNative()) return false;
406  }
407  return true;
408  }
409 
410  Value AsNativeValue(const size_t i) const
411  {
412  OPENVDB_ASSERT(mTypes[i].IsNative());
413  return Value(mArgs[i], mTypes[i].GetUnderlyingType());
414  }
415 
417  {
418  OPENVDB_ASSERT(this->AreNativeValues());
419  NativeArguments args;
420  for (size_t i = 0; i < mArgs.size(); ++i) {
421  args.AddArg(this->AsNativeValue(i));
422  }
423  return args;
424  }
425 
426  const llvm::ArrayRef<llvm::Value*> AsLLVMValues() const { return mArgs; }
427  const ArgInfo& GetArgInfo(size_t pos) const { return mTypes[pos]; }
428  const ArgInfoVector& GetArgInfo() const { return mTypes; }
429 
430  llvm::Value* operator[](size_t pos) const
431  {
432  OPENVDB_ASSERT(pos < mTypes.size());
433  return mArgs[pos];
434  }
435 
436  void AddArg(llvm::Value* val, const ArgInfo& type)
437  {
438 #if LLVM_VERSION_MAJOR <= 15
439  [[maybe_unused]] llvm::Type* base = val->getType();
440  while (base->isPointerTy()) base = base->getContainedType(0);
441  OPENVDB_ASSERT_MESSAGE((base == type.GetUnderlyingType()),
442  "Base type of val does not match stored underlying type");
443 #endif
444  mArgs.emplace_back(val);
445  mTypes.emplace_back(type);
446  }
447 
448  void AddArg(const Value& val)
449  {
450  mArgs.emplace_back(val.GetValue());
451  mTypes.emplace_back(val);
452  }
453 
454  void PrependArg(const Value& val)
455  {
456  this->AddArg(val);
457  std::rotate(mArgs.rbegin(), mArgs.rbegin() + 1, mArgs.rend());
458  std::rotate(mTypes.rbegin(), mTypes.rbegin() + 1, mTypes.rend());
459  }
460 
461 private:
463  ArgInfoVector mTypes;
464 };
465 
466 ///////////////////////////////////////////////////////////////////////////////
467 ///////////////////////////////////////////////////////////////////////////////
468 
469 /// @brief Populate a vector of llvm types from a function signature
470 /// declaration.
471 /// @warning From LLVM 16 onwards, pointer argument type cannot be
472 /// introspected
473 ///
474 /// @param C The llvm context
475 /// @param types A vector of types to populate
476 ///
477 template <typename SignatureT>
478 inline llvm::Type*
479 llvmTypesFromSignature(llvm::LLVMContext& C,
480  std::vector<llvm::Type*>* types = nullptr)
481 {
482  using Traits = FunctionTraits<SignatureT>;
483  using ArgumentIteratorT =
485 
486  if (types) {
487  types->reserve(Traits::N_ARGS);
488  auto callback = [&types, &C](auto type) {
489  using Type = decltype(type);
490  types->emplace_back(LLVMType<Type>::get(C));
491  };
492  ArgumentIteratorT::apply(callback, /*forwards*/true);
493  }
494  using Type = typename Traits::ReturnType;
495  return LLVMType<Type>::get(C);
496 }
497 
498 /// @brief Populate a vector of ArgInfos from a function signature
499 /// declaration.
500 ///
501 /// @param C The llvm context
502 /// @param types A ArgInfoVector to populate
503 ///
504 template <typename SignatureT>
505 inline ArgInfo
506 llvmArgTypesFromSignature(llvm::LLVMContext& C,
507  ArgInfoVector* types = nullptr)
508 {
509  using Traits = FunctionTraits<SignatureT>;
510  using ArgumentIteratorT =
512 
513  if (types)
514  {
515  types->reserve(Traits::N_ARGS);
516  auto callback = [&types, &C](auto type)
517  {
518  using UnderlyingType = std::remove_cv_t<typename RemoveAllPtrTypes<decltype(type)>::Type>;
519  static constexpr auto NPtrs = CountNPtrs<decltype(type)>::value;
520  static constexpr bool IsVoid = std::is_same_v<UnderlyingType, void>;
521  // args can't be void without pts
522  static_assert(!IsVoid || NPtrs > 0);
523  // void* arguments alias to int8_t
524  using Type = std::conditional_t<IsVoid, int8_t, UnderlyingType>;
525  types->emplace_back(LLVMType<Type>::get(C), NPtrs);
526  };
527  ArgumentIteratorT::apply(callback, /*forwards*/true);
528  }
529 
530  using UnderlyingType = std::remove_cv_t<typename RemoveAllPtrTypes<typename Traits::ReturnType>::Type>;
531  static constexpr auto NPtrs = CountNPtrs<typename Traits::ReturnType>::value;
532 
533  if constexpr (std::is_same_v<UnderlyingType, void> && NPtrs > 0)
534  {
535  // if underlying type is void, alias to int8_t if its a void*
536  ArgInfo ret{LLVMType<int8_t>::get(C), NPtrs};
537  ret.SetIsReturn();
538  return ret;
539  }
540  else {
541  ArgInfo ret{LLVMType<UnderlyingType>::get(C), NPtrs};
542  ret.SetIsReturn();
543  return ret;
544  }
545 }
546 
547 
548 /// @brief Generate an LLVM FunctionType from a function signature
549 ///
550 /// @param C The llvm context
551 ///
552 template <typename SignatureT>
553 inline llvm::FunctionType*
554 llvmFunctionTypeFromSignature(llvm::LLVMContext& C)
555 {
556  ArgInfoVector types;
557  ArgInfo returnType =
558  llvmArgTypesFromSignature<SignatureT>(C, &types);
559  return llvm::FunctionType::get(returnType.GetType(),
560  types.AsLLVMTypes(), /*isVarArg=*/false);
561 }
562 
563 /// @brief Print a function signature to the provided ostream.
564 ///
565 /// @param os The stream to print to
566 /// @param types The function argument types
567 /// @param returnType The return type of the function. Must not be a nullptr
568 /// @param name The name of the function. If not provided, the return type
569 /// neighbours the first parenthesis
570 /// @param names Names of the function parameters. If a name is nullptr, it
571 /// skipped
572 /// @param axTypes Whether to try and convert the llvm::Types provided to
573 /// AX types. If false, the llvm types are used.
574 OPENVDB_AX_API void
575 printSignature(std::ostream& os,
576  const ArgInfoVector& types,
577  const ArgInfo& returnType,
578  const char* name = nullptr,
579  const llvm::ArrayRef<const char*>& names = {},
580  const bool axTypes = false);
581 
582 
583 #if LLVM_VERSION_MAJOR <= 15
584 OPENVDB_DEPRECATED_MESSAGE("Switch to AX's internal ArgInfo types for LLVM 16 onwards")
585 OPENVDB_AX_API void
586 printSignature(std::ostream& os,
587  const std::vector<llvm::Type*>& types,
588  const llvm::Type* returnType,
589  const char* name = nullptr,
590  const std::vector<const char*>& names = {},
591  const bool axTypes = false);
592 #endif
593 
594 ///////////////////////////////////////////////////////////////////////////////
595 ////////////////////////////////////////////////////////////////////////////////
596 
597 /// Forward declare builder for private access to Function types
598 struct FunctionBuilder;
599 
600 /// @brief The base/abstract representation of an AX function. Derived classes
601 /// must implement the Function::types call to describe their signature.
603 {
604  using Ptr = std::shared_ptr<Function>;
605 
606  Function(const size_t size, const std::string& symbol)
607  : mSize(size)
608  , mSymbol(symbol)
609  , mAttributes(nullptr)
610  , mNames()
611  , mDeps() {
612  // symbol must be a valid string
613  OPENVDB_ASSERT(!symbol.empty());
614  }
615 
616  virtual ~Function() = default;
617 
618  /// @brief Populate a vector of ArgInfos which describe this function
619  /// signature. This method is used by Function::create,
620  /// Function::print and Function::match.
621  /// @note The variant that takes a vector of ArgInfos is optional with
622  /// LLVM 15 for compatibility but must be implemented with newer
623  /// versions.
624 #if LLVM_VERSION_MAJOR <= 15
625  virtual ArgInfo types(ArgInfoVector&, llvm::LLVMContext&) const
626  {
628  std::string("New AX API for function arguments has been called but has not "
629  "been implemented by function: ") + this->symbol());
630  }
631  OPENVDB_DEPRECATED_MESSAGE("Switch to AX's internal ArgInfo types for LLVM 16 onwards")
632  virtual llvm::Type* types(std::vector<llvm::Type*>&, llvm::LLVMContext&) const = 0;
633 #else
634  virtual ArgInfo types(ArgInfoVector&, llvm::LLVMContext&) const = 0;
635 #endif
636 
637  /// @brief Converts and creates this AX function into a llvm Function.
638  /// @details This method uses the result from Function::types() to construct
639  /// a llvm::FunctionType and a subsequent a llvm::Function. Any
640  /// parameter, return or function attributes are also added to the
641  /// function. If a module is provided, the module if first checked
642  /// to see if the function already exists. If it does, it is
643  /// immediately returned. If the function doesn't exist in the
644  /// module, its prototype is created and also inserted into the end
645  /// of the modules function list. If no module is provided, the
646  /// function is left detached and must be added to a valid Module
647  /// to be callable.
648  /// @warning If a module is not provided, the caller takes ownership of the
649  /// returned function and is responsible for deallocating it.
650  /// @note The body of the function is left to derived classes to
651  /// implement. As you need a Module to generate the prototype/body,
652  /// this function serves two purposes. The first is to return the
653  /// detached function signature if only a context is provided.
654  /// The second is to ensure the function prototype and body (if
655  /// required) is inserted into the module prior to returning.
656  /// @note It is possible to end up with function symbol collisions if you
657  /// do not have unique function symbols in your module
658  ///
659  /// @param C The LLVM Context
660  /// @param M The Module to write the function to
661  virtual llvm::Function*
662  create(llvm::LLVMContext& C, llvm::Module* M = nullptr) const;
663 
664  /// @brief Convenience method which always uses the provided module to find
665  /// the function or insert it if necessary.
666  /// @param M The llvm::Module to use
667  llvm::Function* create(llvm::Module& M) const {
668  return this->create(M.getContext(), &M);
669  }
670 
671  /// @brief Convenience method for calling M.getFunction(symbol). Returns a
672  /// nullptr if the function has not yet been created or if it is
673  /// embedded IR.
674  /// @param M The llvm::Module to use
675  llvm::Function* get(const llvm::Module& M) const;
676 
677  /// @brief Uses the IRBuilder to create a call to this function with the
678  /// given arguments, creating the function and inserting it into the
679  /// IRBuilder's Module if necessary (through Function::create).
680  /// Returns the result of the function call which can be a nullptr
681  /// if the function is a non-sret void call.
682  /// @note The IRBuilder must have a valid llvm Module/Function/Block
683  /// attached
684  /// @note If the number of provided arguments do not match the size of the
685  /// current function, invalid IR will be generated.
686  /// @note If the provided argument types do not match the current function
687  /// and cast is false, invalid IR will be generated. Additionally,
688  /// invalid IR will be generated if cast is true but no valid cast
689  /// exists for a given argument.
690  /// @note When casting arguments, the readonly flags of the function are
691  /// not checked (unlike Function::match). Casting an argument will
692  /// cause a new copy of the argument to be created and passed to the
693  /// function. These new values do not propagate back any changes to
694  /// the original argument. Separate functions for all writable
695  /// argument types must be created.
696  ///
697  /// @param args The llvm Value arguments to call this function with
698  /// @param B The llvm IRBuilder
699  /// @param cast Whether to allow implicit casting of arguments
700 #if LLVM_VERSION_MAJOR <= 15
701  OPENVDB_DEPRECATED_MESSAGE("Function::call which takes llvm::Value's and "
702  "supports casting is incompatible with LLVM 16+ and will be removed.")
703  virtual llvm::Value*
704  call(const std::vector<llvm::Value*>& args,
705  llvm::IRBuilder<>& B,
706  const bool cast) const;
707 #endif
708  /// From LLVM 16 onwards, this version of call does not support argument
709  /// casting. This must be performed using the NativeArguments struct
710  virtual llvm::Value*
711  call(const std::vector<llvm::Value*>& args,
712  llvm::IRBuilder<>& B) const;
713 
714  virtual Value call(const Arguments& args, llvm::IRBuilder<>& B) const;
715 
716  Value call(const NativeArguments& args, llvm::IRBuilder<>& B) const
717  {
718  return this->call(Arguments(args.Cast(*this, B)), B);
719  }
720 
721  /// @brief The result type from calls to Function::match
722  /// @note Function::match cannot return Ambiguous - this is only returned
723  /// by the FunctionGroup API.
725  {
726  None = 0, // Mismatching argument sizes
727  Size, // Correct number of arguments but incompatible types
728  Implicit, // Correct number of arguments and castable types
729  Ambiguous, // Correct number of arguments and castable types but multiple available signatures
730  Explicit // Correct number of arguments and types match exactly
731  };
732 
733  /// @brief The base implementation for determining how a vector of llvm
734  /// arguments translates to this functions signature. Returns an
735  /// enum which represents the available mapping.
736  /// @details This method calls types() to figure out the function signature,
737  /// then compares each argument type to the type in the input
738  /// vector. If the types match exactly, an Explicit match is found.
739  /// If the sizes of the inputs and signature differ, no match is
740  /// found and None is returned. If however, the sizes match and
741  /// there exists a valid implicit cast from the input type to the
742  /// signature type for every input, an Implicit match is returned.
743  /// Finally, if the sizes match but there is no implicit cast
744  /// mapping, Size is returned.
745  /// i8 -> i32 : Implicit
746  /// i32 -> i32 : Explicit
747  /// str -> i32 : Size
748  /// (i32,i32) -> i32 : None
749  /// Never returns Ambiguous (this state is used by FunctionGroup)
750  /// @note Due to the way CFunctionSRet is implemented, the LLVM Context
751  /// must be provided in case we have a zero arg function signature
752  /// with a SRET.
753  /// @param inputs The input types
754  /// @param C The LLVM Context
755  virtual SignatureMatch match(const ArgInfoVector& inputs, llvm::LLVMContext& C) const;
756 #if LLVM_VERSION_MAJOR <= 15
757  OPENVDB_DEPRECATED_MESSAGE("Switch to AX's internal ArgInfo types for LLVM 16 onwards")
758  virtual SignatureMatch match(const std::vector<llvm::Type*>& inputs, llvm::LLVMContext& C) const;
759 #endif
760 
761  /// @brief The number of arguments that this function has
762  inline size_t size() const { return mSize; }
763 
764  /// @brief The function symbol name.
765  /// @details This will be used as its identifier in IR and must be unique.
766  inline const char* symbol() const { return mSymbol.c_str(); }
767 
768  /// @brief Returns the descriptive name of the given argument index
769  /// @details If the index is greater than the number of arguments, an empty
770  /// string is returned.
771  ///
772  /// @param idx The index of the argument
773  inline const char* argName(const size_t idx) const
774  {
775  return idx < mNames.size() ? mNames[idx] : "";
776  }
777 
778  /// @brief Print this function's signature to the provided ostream.
779  /// @details This is intended to return a descriptive front end user string
780  /// rather than the function's IR representation. This function is
781  /// virtual so that derived classes can customize how they present
782  /// frontend information.
783  /// @sa printSignature
784  ///
785  /// @param C The llvm context
786  /// @param os The ostream to print to
787  /// @param name The name to insert into the description.
788  /// @param axTypes Whether to print llvm IR or AX Types.
789  virtual void print(llvm::LLVMContext& C,
790  std::ostream& os,
791  const char* name = nullptr,
792  const bool axTypes = true) const;
793 
794  const SmallArgumentVector<const char*>& dependencies() const { return mDeps; }
795 
796  /// Deprecated builder methods, no longer public
797 
798  OPENVDB_DEPRECATED_MESSAGE("This method incorrectly returns the attributes "
799  "of the function set by the FunctionBuilder, not by the codegen. To "
800  "inspect function attributes, retrieve the created function from the "
801  "llvm::Module.")
802  inline bool hasParamAttribute(const size_t i,
803  const llvm::Attribute::AttrKind& kind) const
804  {
805  if (!mAttributes) return false;
806  const auto iter = mAttributes->mParamAttrs.find(i);
807  if (iter == mAttributes->mParamAttrs.end()) return false;
808  const auto& vec = iter->second;
809  return std::find(vec.begin(), vec.end(), kind) != vec.end();
810  }
811 
812  OPENVDB_DEPRECATED_MESSAGE("Use the FunctionBuilder to construct Functions")
813  inline void setArgumentNames(std::vector<const char*> names)
814  {
815  mNames.assign(names.begin(), names.end());
816  }
817 
818  OPENVDB_DEPRECATED_MESSAGE("Use the FunctionBuilder to construct Functions")
819  inline void setDependencies(std::vector<const char*> deps)
820  {
821  mDeps.assign(deps.begin(), deps.end());
822  }
823 
824  OPENVDB_DEPRECATED_MESSAGE("Use the FunctionBuilder to construct Functions")
825  inline void setFnAttributes(const std::vector<llvm::Attribute::AttrKind>& in)
826  {
827  this->attrs().mFnAttrs.assign(in.begin(), in.end());
828  }
829 
830  OPENVDB_DEPRECATED_MESSAGE("Use the FunctionBuilder to construct Functions")
831  inline void setRetAttributes(const std::vector<llvm::Attribute::AttrKind>& in)
832  {
833  this->attrs().mRetAttrs.assign(in.begin(), in.end());
834  }
835 
836  OPENVDB_DEPRECATED_MESSAGE("Use the FunctionBuilder to construct Functions")
837  inline void setParamAttributes(const size_t i,
838  const std::vector<llvm::Attribute::AttrKind>& in)
839  {
840  this->attrs().mParamAttrs[i].assign(in.begin(), in.end());
841  }
842 
843 protected:
844 
845 #if LLVM_VERSION_MAJOR <= 15
846  /// @brief Cast the provided arguments to the given type as supported by
847  /// implicit casting of function types. If the types already match
848  /// OR if a cast cannot be performed, nothing is done to the argument.
849  /// @todo This should really be generalized out for Function::call and
850  /// Function::match to both use. However, due to SRET functions,
851  /// this logic must be performed somewhere in the Function class
852  /// hierarchy and not in FunctionGroup
853  static void cast(std::vector<llvm::Value*>& args,
854  const std::vector<llvm::Type*>& types,
855  llvm::IRBuilder<>& B);
856 #endif
857 
858 private:
859  friend FunctionBuilder;
860 
861  enum class Type {
862  kNormal,
863  kBuiltin,
864  kKernel
865  };
866 
867  struct Attributes
868  {
870  std::map<size_t, SmallArgumentVector<llvm::Attribute::AttrKind>> mParamAttrs;
871  bool mReadOnly {false};
872  Type mType {Type::kNormal};
873  };
874 
875  inline Attributes& attrs()
876  {
877  if (!mAttributes) mAttributes.reset(new Attributes());
878  return *mAttributes;
879  }
880 
881  /// @brief Temporary method until we move to ArgInfo introspection
882  bool IsParamReadOnly(const size_t idx) const
883  {
884  if (!mAttributes) return false; // can't be certain if no attrs set
885  if (mAttributes->mReadOnly) return true;
886  // @todo REMOVE - switch to using writable info on the ArgInfo types
887  const auto iter = mAttributes->mParamAttrs.find(idx);
888  if (iter == mAttributes->mParamAttrs.end()) return false;
889  const auto& vec = iter->second;
890  return std::find(vec.begin(), vec.end(),
891  llvm::Attribute::AttrKind::ReadOnly) != vec.end();
892  }
893 
894  llvm::AttributeList flattenAttrs(llvm::Function* F) const;
895 
896 private:
897  const size_t mSize;
898  const std::string mSymbol;
899  std::unique_ptr<Attributes> mAttributes;
902 };
903 
904 /// @brief Templated interface class for SRET functions. This struct provides
905 /// the interface for functions that wish to return arrays (vectors or
906 /// matrices) by internally remapping the first argument for the user.
907 /// As far as LLVM and any bindings are concerned, the function
908 /// signature remains unchanged - however the first argument becomes
909 /// "invisible" to the user and is instead allocated by LLVM before the
910 /// function is executed. Importantly, the argument has no impact on
911 /// the user facing AX signature and doesn't affect declaration selection.
912 /// @note This class is not intended to be instantiated directly, but instead
913 /// used by derived implementation which hold a valid implementations
914 /// of member functions required to create a llvm::Function (such as
915 /// Function::types and Function::call). This exists as an interface to
916 /// avoid virtual inheritance.
917 ///
918 template <typename SignatureT, typename DerivedFunction>
919 struct SRetFunction : public DerivedFunction
920 {
921  using Ptr = std::shared_ptr<SRetFunction<SignatureT, DerivedFunction>>;
923 
924  // check there actually are arguments
925  static_assert(Traits::N_ARGS > 0,
926  "SRET Function object has been setup with the first argument as the return "
927  "value, however the provided signature is empty.");
928 
929  // check no return value exists
930  static_assert(std::is_same<typename Traits::ReturnType, void>::value,
931  "SRET Function object has been setup with the first argument as the return "
932  "value and a non void return type.");
933 
934 private:
935 
936  using FirstArgument = typename Traits::template Arg<0>::Type;
937  static_assert(std::is_pointer<FirstArgument>::value,
938  "SRET Function object has been setup with the first argument as the return "
939  "value, but this argument it is not a pointer type.");
940  static_assert(!std::is_const_v<FirstArgument>,
941  "SRET Function object has been setup with the first argument as the return "
942  "value, but this argument is const.");
943  using SRetType = typename std::remove_pointer<FirstArgument>::type;
944 
945 public:
946 
947  /// @brief Overide the ArgInfo type method. This does NOT change the arg
948  /// order, it simply marks the first argument as a return argument. Note
949  /// that the void ret type is also left as a return type.
950  ArgInfo types(ArgInfoVector& args, llvm::LLVMContext& C) const override
951  {
952  ArgInfo ret = DerivedFunction::types(args, C);
953  OPENVDB_ASSERT(!args.empty());
954  OPENVDB_ASSERT(ret.IsVoid());
955  OPENVDB_ASSERT(!args[0].IsVoid());
956  OPENVDB_ASSERT(args[0].IsPtr());
957  args[0].SetIsReturn();
958  return ret;
959  }
960 
961  /// @brief Override of match which inserts the SRET type such that the base
962  /// class methods ignore it.
964  const ArgInfoVector& args,
965  llvm::LLVMContext& C) const override
966  {
967  ArgInfoVector inputs(args);
968  // Create a dummy sret ptr type for derived match impls
969  llvm::Type* stype = LLVMType<SRetType>::get(C);
970  // llvm::Constant* zero = llvmConstant(0, llvm::Type::getInt64Ty(C));
971  // llvm::Constant* dummy = llvm::ConstantExpr::getPointerCast(zero, stype->getPointerTo());
972  inputs.emplace_back(stype, 1);
973  inputs.back().SetIsReturn();
974  std::rotate(inputs.rbegin(), inputs.rbegin() + 1, inputs.rend());
975  return DerivedFunction::match(inputs, C);
976  }
977 
978  /// @brief Override of call which allocates the required SRET llvm::Value
979  /// for this function.
980  /// @note Unlike other function where the returned llvm::Value* is a
981  /// llvm::CallInst (which also represents the return value),
982  /// SRET functions return the allocated 1st argument i.e. not a
983  /// llvm::CallInst
984  llvm::Value*
985  call(const std::vector<llvm::Value*>& args,
986  llvm::IRBuilder<>& B) const override
987  {
988  // append return value and right rotate
989  std::vector<llvm::Value*> inputs(args);
990  llvm::Type* sret = LLVMType<SRetType>::get(B.getContext());
991  inputs.emplace_back(insertStaticAlloca(B, sret));
992  std::rotate(inputs.rbegin(), inputs.rbegin() + 1, inputs.rend());
993  DerivedFunction::call(inputs, B);
994  return inputs.front();
995  }
996 
997  Value call(const Arguments& args, llvm::IRBuilder<>& B) const override
998  {
999  // append return value and right rotate
1000  Arguments inputs(args);
1001  Value sret = Value::Alloc(B, LLVMType<SRetType>::get(B.getContext()));
1002  inputs.PrependArg(sret);
1003  DerivedFunction::call(inputs, B);
1004  return sret;
1005  }
1006 
1007  /// @brief Override of print to avoid printing out the SRET type
1008  void print(llvm::LLVMContext& C,
1009  std::ostream& os,
1010  const char* name = nullptr,
1011  const bool axTypes = true) const override
1012  {
1013  ArgInfoVector current;
1014  ArgInfo ret = this->types(current, C);
1015  // left rotate
1016  std::rotate(current.begin(), current.begin() + 1, current.end());
1017  ret = current.back();
1018  current.pop_back();
1019 
1021  names.reserve(this->size());
1022  for (size_t i = 0; i < this->size()-1; ++i) {
1023  names.emplace_back(this->argName(i));
1024  }
1025  printSignature(os, current, ret, name, names, axTypes);
1026  }
1027 
1028 #if LLVM_VERSION_MAJOR <= 15
1029  /// @note Bring in deprecated type methods
1030  using Function::types;
1031 
1032  /// @note This is deprecated! Omitting the warning as it invokes the
1033  /// parent function which is also deprecated. Use NativeArguments to
1034  /// perform argument casting
1035  Function::SignatureMatch match(const std::vector<llvm::Type*>& args,
1036  llvm::LLVMContext& C) const override
1037  {
1038  // append return type and right rotate
1039  std::vector<llvm::Type*> inputs(args);
1040  inputs.emplace_back(LLVMType<SRetType*>::get(C));
1041  std::rotate(inputs.rbegin(), inputs.rbegin() + 1, inputs.rend());
1042  return DerivedFunction::match(inputs, C);
1043  }
1044 
1045  /// @note This is deprecated! Omitting the warning as it invokes the
1046  /// parent function which is also deprecated. Use NativeArguments to
1047  /// perform argument casting
1048  llvm::Value*
1049  call(const std::vector<llvm::Value*>& args,
1050  llvm::IRBuilder<>& B,
1051  const bool cast) const override
1052  {
1053  // append return value and right rotate
1054  std::vector<llvm::Value*> inputs(args);
1055  llvm::Type* sret = LLVMType<SRetType>::get(B.getContext());
1056  inputs.emplace_back(insertStaticAlloca(B, sret));
1057  std::rotate(inputs.rbegin(), inputs.rbegin() + 1, inputs.rend());
1058  DerivedFunction::call(inputs, B, cast);
1059  return inputs.front();
1060  }
1061 #endif
1062 
1063 protected:
1064  /// @brief Forward all arguments to the derived class
1065  template <typename ...Args>
1066  SRetFunction(Args&&... ts) : DerivedFunction(ts...) {}
1067 };
1068 
1069 /// @brief The base class for all C bindings.
1070 struct CFunctionBase : public Function
1071 {
1072  using Ptr = std::shared_ptr<CFunctionBase>;
1073 
1074  ~CFunctionBase() override = default;
1075 
1076  /// @brief Returns the global address of this function.
1077  /// @note This is only required for C bindings.
1078  virtual uint64_t address() const = 0;
1079 
1080  inline void setConstantFold(bool on) { mConstantFold = on; }
1081  inline bool hasConstantFold() const { return mConstantFold; }
1082 
1083 #if LLVM_VERSION_MAJOR <= 15
1085  inline virtual llvm::Value* fold(
1086  const std::vector<llvm::Value*>&,
1087  llvm::LLVMContext&) const
1088  {
1089  return nullptr;
1090  }
1091 #else
1092  inline virtual llvm::Value* fold(
1093  const llvm::ArrayRef<llvm::Value*>&,
1094  llvm::LLVMContext&) const
1095  {
1096  return nullptr;
1097  }
1098 #endif
1099 
1100 protected:
1101  CFunctionBase(const size_t size,
1102  const std::string& symbol)
1103  : Function(size, symbol)
1104  , mConstantFold(false) {}
1105 
1106 private:
1107  bool mConstantFold;
1108 };
1109 
1110 /// @brief Represents a concrete C function binding.
1111 ///
1112 /// @note This struct is templated on the signature to allow for evaluation of
1113 /// the arguments to llvm types from any llvm context.
1114 ///
1115 template <typename SignatureT>
1116 struct CFunction : public CFunctionBase
1117 {
1119  using Ptr = std::shared_ptr<CFunctionT>;
1121 
1122  // Assert that the return argument is not a pointer (relaxed for void* for mallocs).
1123  // Note that this is relaxed for IR functions where it's allowed if the function is
1124  // forcefully inlined.
1125  static_assert(std::is_same<typename Traits::ReturnType, void*>::value ||
1126  !std::is_pointer<typename Traits::ReturnType>::value,
1127  "CFunction object has been setup with a pointer return argument. C bindings "
1128  "cannot return memory locations to LLVM - Consider using a CFunctionSRet.");
1129 
1130  CFunction(const std::string& symbol, SignatureT* function)
1131  : CFunctionBase(Traits::N_ARGS, symbol)
1132  , mFunction(function) {}
1133 
1134  ~CFunction() override = default;
1135 
1136  inline ArgInfo types(ArgInfoVector& types, llvm::LLVMContext& C) const override
1137  {
1138  return llvmArgTypesFromSignature<SignatureT>(C, &types);
1139  }
1140 
1141  inline uint64_t address() const override final
1142  {
1143  return reinterpret_cast<uint64_t>(mFunction);
1144  }
1145 
1146  llvm::Value*
1147  call(const std::vector<llvm::Value*>& args,
1148  llvm::IRBuilder<>& B) const override
1149  {
1150  llvm::Value* result = this->fold(args, B.getContext());
1151  if (result) return result;
1152  return Function::call(args, B);
1153  }
1154 
1155  Value call(const Arguments& args, llvm::IRBuilder<>& B) const override
1156  {
1157  llvm::Constant* result = this->fold(args.AsLLVMValues(), B.getContext());
1158  if (result) return Value(result);
1159  return Function::call(args, B);
1160  }
1161 
1162 #if LLVM_VERSION_MAJOR <= 15
1163  llvm::Constant* fold(const std::vector<llvm::Value*>& args, llvm::LLVMContext& C) const override final
1164 #else
1165  llvm::Constant* fold(const llvm::ArrayRef<llvm::Value*>& args, llvm::LLVMContext& C) const override final
1166 #endif
1167  {
1168  if (!this->hasConstantFold()) return nullptr;
1170  for (auto& value : args) {
1171  if (!llvm::isa<llvm::Constant>(value)) return nullptr;
1172  constants.emplace_back(llvm::cast<llvm::Constant>(value));
1173  }
1174  // no guarantee that fold() will be able to cast all arguments
1175  return ConstantFolder<SignatureT>::fold(constants, *mFunction, C);
1176  }
1177 
1178 #if LLVM_VERSION_MAJOR <= 15
1179  inline llvm::Type* types(std::vector<llvm::Type*>& types, llvm::LLVMContext& C) const override
1180  {
1181  return llvmTypesFromSignature<SignatureT>(C, &types);
1182  }
1183 
1184  /// @note This is deprecated! Omitting the warning as it invokes the
1185  /// parent function which is also deprecated. Use NativeArguments to
1186  /// perform argument casting
1187  llvm::Value*
1188  call(const std::vector<llvm::Value*>& args,
1189  llvm::IRBuilder<>& B,
1190  const bool cast) const override
1191  {
1192  llvm::Value* result = this->fold(args, B.getContext());
1193  if (result) return result;
1194  return Function::call(args, B, cast);
1195  }
1196 #endif
1197 
1198 private:
1199  SignatureT* mFunction;
1200 };
1201 
1202 /// @brief The base/abstract definition for an IR function.
1204 {
1205  using Ptr = std::shared_ptr<IRFunctionBase>;
1206 
1207  /// @brief The IR callback function which will write the LLVM IR for this
1208  /// function's body.
1209  /// @details The first argument is the vector of functional arguments. i.e.
1210  /// a representation of the value that the callback has been invoked
1211  /// with.
1212  /// The last argument is the IR builder which should be used to
1213  /// generate the function body IR.
1214  /// @note You can return a ret void instruction, an actual value or
1215  /// Value::Invalid() which will cause the function framework to
1216  /// insert a ret void if necessary.
1217  using GeneratorNativeCb = std::function<Value
1218  (const NativeArguments&, llvm::IRBuilder<>&)>;
1219 
1220  using GeneratorArgumentsCb = std::function<Value
1221  (const Arguments&, llvm::IRBuilder<>&)>;
1222 
1223  /// @brief Legacy callback, will eventually be deprecated in favour of
1224  /// using the GeneratorArgumentsCb
1225  using GeneratorCb = std::function<llvm::Value*
1226  (const std::vector<llvm::Value*>&,
1227  llvm::IRBuilder<>&)>;
1228 
1229  /// @brief Enable or disable the embedding of IR. Embedded IR is currently
1230  /// required for function which use parent function parameters.
1231  inline void setEmbedIR(bool on) { mEmbedIR = on; }
1232  inline bool hasEmbedIR() const { return mEmbedIR; }
1233 
1234  /// @brief Override for the creation of an IR function. This ensures that
1235  /// the body and prototype of the function are generated if a Module
1236  /// is provided.
1237  /// @note A nullptr is returned if mEmbedIR is true and no action is
1238  /// performed.
1239  /// @note Throws if this function has been initialized with a nullptr
1240  /// generator callback. In this case, the function prototype will
1241  /// be created, but not the function body.
1242  /// @note Throws if the return type of the generator callback does not
1243  /// match the function prototype. In this case, both the prototype
1244  /// and the function body will be created and inserted, but the IR
1245  /// will be invalid.
1246  llvm::Function*
1247  create(llvm::LLVMContext& C, llvm::Module* M) const override;
1248 
1249  /// @brief Override for call, which is only necessary if mEmbedIR is true,
1250  /// as the IR generation for embedded functions is delayed until
1251  /// the function is called. If mEmbedIR is false, this simply calls
1252  /// Function::call
1253  llvm::Value*
1254  call(const std::vector<llvm::Value*>& args,
1255  llvm::IRBuilder<>& B) const override;
1256 
1257  Value call(const Arguments& args, llvm::IRBuilder<>&) const override;
1258 
1259 #if LLVM_VERSION_MAJOR <= 15
1260  // @note This is deprecated! Omitting the warning as it invokes the
1261  // parent function which is also deprecated
1262  llvm::Value*
1263  call(const std::vector<llvm::Value*>& args,
1264  llvm::IRBuilder<>& B,
1265  const bool cast) const override;
1266 #endif
1267 
1268 protected:
1269 
1270  // @todo This should ideally live in FunctionGroup::execute, but the return
1271  // type is allowed to differ for sret C bindings.
1272  inline void
1273  verifyResultType(const llvm::Type* result, const llvm::Type* expected) const
1274  {
1275  if (result == expected) return;
1276  std::string source, target;
1277  if (result) llvmTypeToString(result, source);
1278  llvmTypeToString(expected, target);
1279  OPENVDB_THROW(AXCodeGenError, "Function \"" + std::string(this->symbol()) +
1280  "\" has been invoked with a mismatching return type. Expected: \"" +
1281  target + "\", got \"" + source + "\".");
1282  }
1283  IRFunctionBase(const std::string& symbol,
1284  const GeneratorCb& gen,
1285  const size_t size)
1286  : Function(size, symbol)
1287  , mGen([this, gen](const Arguments& args, llvm::IRBuilder<>& B) {
1288  llvm::Value* result = gen(args.AsLLVMValues(), B);
1289  if (!result) return Value::Invalid();
1290  // For older callbacks, we have to figure out the type from the
1291  // function signature if the return type is a ptr (from LLVM 16
1292  // onwards)
1293  if (result->getType()->isPointerTy())
1294  {
1295 #if LLVM_VERSION_MAJOR <= 15
1296  return Value(result, result->getType()->getPointerElementType());
1297 #else
1298  ArgInfoVector unused;
1299  ArgInfo r = this->types(unused, result->getContext());
1300  return Value(result, r.GetUnderlyingType());
1301 #endif
1302  }
1303  else {
1304  // otherwise, can we introspec the type directly
1305  return Value(result, result->getType());
1306  }
1307  })
1308  , mEmbedIR(false) {}
1309  IRFunctionBase(const std::string& symbol,
1310  const GeneratorNativeCb& gen,
1311  const size_t size)
1312  : Function(size, symbol)
1313  , mGen([gen](const Arguments& args, llvm::IRBuilder<>& B) {
1314  OPENVDB_ASSERT(args.AreNativeValues());
1315  return gen(args.AsNativeValues(), B);
1316  })
1317  , mEmbedIR(false) {}
1318  IRFunctionBase(const std::string& symbol,
1319  const GeneratorArgumentsCb& gen,
1320  const size_t size)
1321  : Function(size, symbol)
1322  , mGen(gen)
1323  , mEmbedIR(false) {}
1324  ~IRFunctionBase() override = default;
1325 
1327  bool mEmbedIR;
1328 };
1329 
1330 /// @brief Represents a concrete IR function.
1331 template <typename SignatureT>
1333 {
1335  using Ptr = std::shared_ptr<IRFunction>;
1336 
1337  IRFunction(const std::string& symbol, const GeneratorCb& gen)
1338  : IRFunctionBase(symbol, gen, Traits::N_ARGS) {}
1339  IRFunction(const std::string& symbol, const GeneratorNativeCb& gen)
1340  : IRFunctionBase(symbol, gen, Traits::N_ARGS) {}
1341  IRFunction(const std::string& symbol, const GeneratorArgumentsCb& gen)
1342  : IRFunctionBase(symbol, gen, Traits::N_ARGS) {}
1343 
1344  inline ArgInfo types(ArgInfoVector& types, llvm::LLVMContext& C) const override
1345  {
1346  return llvmArgTypesFromSignature<SignatureT>(C, &types);
1347  }
1348 
1349 #if LLVM_VERSION_MAJOR <= 15
1350  inline llvm::Type*
1351  types(std::vector<llvm::Type*>& types, llvm::LLVMContext& C) const override
1352  {
1353  return llvmTypesFromSignature<SignatureT>(C, &types);
1354  }
1355 #endif
1356 };
1357 
1358 /// @brief Represents a concrete C function binding with the first argument as
1359 /// its return type.
1360 template <typename SignatureT>
1361 struct CFunctionSRet : public SRetFunction<SignatureT, CFunction<SignatureT>>
1362 {
1364  CFunctionSRet(const std::string& symbol, const SignatureT function)
1365  : BaseT(symbol, function) {}
1366  ~CFunctionSRet() override = default;
1367 };
1368 
1369 /// @brief Represents a concrete IR function with the first argument as
1370 /// its return type.
1371 template <typename SignatureT>
1372 struct IRFunctionSRet : public SRetFunction<SignatureT, IRFunction<SignatureT>>
1373 {
1375  IRFunctionSRet(const std::string& symbol,
1376  const IRFunctionBase::GeneratorCb& gen)
1377  : BaseT(symbol, gen) {}
1378  IRFunctionSRet(const std::string& symbol,
1380  : BaseT(symbol, gen) {}
1381  IRFunctionSRet(const std::string& symbol,
1383  : BaseT(symbol, gen) {}
1384 
1385  ~IRFunctionSRet() override = default;
1386 };
1387 
1388 /// @brief A group of functions which all have the same name but different
1389 /// signatures. For example:
1390 /// float abs(float)
1391 /// double abs(double)
1392 /// As well as serving as a way of grouping common functions, this class
1393 /// provides an API for selecting the best possible function signature,
1394 /// should a match exist, against a provided set of argument types.
1396 {
1397  using Ptr = std::shared_ptr<FunctionGroup>;
1398  using UniquePtr = std::unique_ptr<FunctionGroup>;
1399  using FunctionList = std::vector<Function::Ptr>;
1400 
1401  FunctionGroup(const char* name,
1402  const char* doc,
1403  const FunctionList& list)
1404  : mName(name)
1405  , mDoc(doc)
1406  , mFunctionList(list) {}
1407  ~FunctionGroup() = default;
1408 
1409  /// @brief Verify the function signatures in this group.
1410  bool HasUniqueTypeSignatures(llvm::LLVMContext& C) const;
1411 
1412  /// @brief Given a vector of llvm types, automatically returns the best
1413  /// possible function declaration from the stored function list. The
1414  /// 'best' declaration is determined by the provided types
1415  /// compatibility to each functions signature.
1416  /// @note Returns a nullptr if no compatible match was found or if the
1417  /// function list is empty. A compatible match is defined as an
1418  /// Explicit, Implicit or Ambiguous match (where the latter returns
1419  /// the first matched implicit function where other implicit
1420  /// matches exist).
1421  ///
1422  /// @note If multiple implicit matches are found, the first match is
1423  /// returned and 'type' is set to Ambiguous (if provided).
1424  /// @warning All funcions in this group must implement the types(ArgInfo)
1425  /// virtual function, which is optional in LLVM 15 but required from
1426  /// LLVM 16.
1427  ///
1428  /// @param types A vector of types representing the function argument types
1429  /// @param C The llvm context
1430  /// @param type If provided, type is set to the type of match that occurred
1431  std::pair<const Function*, Function::SignatureMatch>
1432  match(const ArgInfoVector& args, llvm::LLVMContext& C) const;
1433 
1434  /// @brief Given a set of Arguments, find an EXPLICIT signature match,
1435  /// generate and execute the function body. If no explicit match
1436  /// exists, Value::Invalid() is returned.
1437  /// @note To ensure something is matched/executed, consider calling
1438  /// match() and Function::call instead. This method should only be
1439  /// used by internal methods that can assert an explicit match
1440  /// exists.
1441  ///
1442  /// @param args Function arguments
1443  /// @param B The current llvm IRBuilder
1444  Value execute(const Arguments& args, llvm::IRBuilder<>& B) const;
1445 
1446  /// @brief Given a set of NativeArguments, find the best possible function
1447  /// signature, generate and execute the function body. Returns the
1448  /// return value of the function or Value::Invalid() if no Explicit
1449  /// or Implicit match is found.
1450  /// @note This function will throw if no valid return is provided by the
1451  /// matched declaration implementation.
1452  ///
1453  /// @param args Natively supported function arguments
1454  /// @param B The current llvm IRBuilder
1455  Value execute(const NativeArguments& args, llvm::IRBuilder<>& B) const;
1456 
1457  /// @brief Accessor to the underlying function signature list
1458  inline const FunctionList& list() const { return mFunctionList; }
1459  const char* name() const { return mName; }
1460  const char* doc() const { return mDoc; }
1461 
1462 #if LLVM_VERSION_MAJOR <= 15
1463  /// @warning Does not support detecting Ambiguous functions (returns Implicit
1464  /// in these cases).
1465  OPENVDB_DEPRECATED_MESSAGE("Switch to AX's internal ArgInfo types for LLVM 16 onwards")
1466  const Function*
1467  match(const std::vector<llvm::Type*>& types,
1468  llvm::LLVMContext& C,
1469  Function::SignatureMatch* type = nullptr) const;
1470 
1471  OPENVDB_DEPRECATED_MESSAGE("FunctionGroup::execute which takes llvm::Value's and "
1472  "supports argument matching/casting is incompatible with LLVM 16+ and will be "
1473  "removed.")
1474  llvm::Value*
1475  execute(const std::vector<llvm::Value*>& args,
1476  llvm::IRBuilder<>& B) const;
1477 
1478  /// @brief Given a vector of llvm values, find the best possible function
1479  /// signature, generate and execute the function body. Returns the
1480  /// Function that was selected and executed or a nullptr if no
1481  /// valid match was found. Sets the result variable to the return
1482  /// value of the function (nullptr if void). If no match is found,
1483  /// the result variable if left unset.
1484  /// @note This function will throw if no valid return is provided by the
1485  /// matched declaration implementation.
1486  ///
1487  /// @param args A vector of values representing the function arguments
1488  /// @param B The current llvm IRBuilder
1489  /// @param result The result to set. nullptr on void return.
1490  /// @return The matched function. nullptr if no match was found
1491  OPENVDB_DEPRECATED_MESSAGE("FunctionGroup::execute which takes llvm::Value's and "
1492  "supports argument matching/casting is incompatible with LLVM 16+ and will be "
1493  "removed.")
1494  const Function*
1495  execute(const std::vector<llvm::Value*>& args,
1496  llvm::IRBuilder<>& B,
1497  llvm::Value*& result) const;
1498 #endif
1499 
1500 private:
1501  const char* mName;
1502  const char* mDoc;
1503  const FunctionList mFunctionList;
1504 };
1505 
1506 /// @brief The FunctionBuilder class provides a builder pattern framework to
1507 /// allow easy and valid construction of AX functions. There are a
1508 /// number of complex tasks which may need to be performed during
1509 /// construction of C or IR function which are delegated to this
1510 /// builder, whilst ensuring that the constructed functions are
1511 /// guaranteed to be valid.
1512 /// @details Use the FunctionBuilder::addSignature methods to append function
1513 /// signatures. Finalize the group of functions with
1514 /// FunctionBuilder::get.
1516 {
1518  C, IR, Any
1519  };
1520 
1521  struct Settings
1522  {
1523  using Ptr = std::shared_ptr<Settings>;
1524 
1525  inline bool isDefault() const
1526  {
1527  if (mNames) return false;
1528  if (mType != Function::Type::kNormal) return false;
1529  if (!mDeps.empty()) return false;
1530  if (mConstantFold || mEmbedIR || mReadOnly) return false;
1531  if (!mFnAttrs.empty()) return false;
1532  if (!mRetAttrs.empty()) return false;
1533  if (!mParamAttrs.empty()) return false;
1534  return true;
1535  }
1536 
1537  std::unique_ptr<SmallArgumentVector<const char*>> mNames = nullptr;
1539  bool mConstantFold = false;
1540  bool mEmbedIR = false;
1541  bool mReadOnly = false;
1542  Function::Type mType = Function::Type::kNormal;
1545  std::map<size_t, SmallArgumentVector<llvm::Attribute::AttrKind>> mParamAttrs = {};
1546  };
1547 
1548  FunctionBuilder(const char* name)
1549  : mName(name)
1550  , mCurrentSettings(std::make_shared<Settings>()) {}
1551 
1552  // C-Binding
1553  template <typename Signature, bool SRet = false>
1554  inline FunctionBuilder&
1555  addSignature(const Signature* ptr, const char* symbol = nullptr)
1556  {
1557  using CFType = typename std::conditional
1559  const std::string s = symbol ? symbol : this->genSymbol<Signature>();
1560  this->addSignatureImpl<CFType>(s, ptr);
1561  return *this;
1562  }
1563 
1564  // Non native Binding
1565  template <typename Signature, bool SRet = false>
1566  inline FunctionBuilder&
1568  const char* symbol = nullptr)
1569  {
1570  using IRFType = typename std::conditional
1572  OPENVDB_ASSERT(!(SRet && mCurrentSettings->mReadOnly));
1573  const std::string s = symbol ? symbol : this->genSymbol<Signature>();
1574  this->addSignatureImpl<IRFType>(s, cb);
1575  return *this;
1576  }
1577 
1578  // Non native Binding
1579  template <typename Signature, bool SRet = false>
1580  inline FunctionBuilder&
1581  addSignature(const IRFunctionBase::GeneratorCb& cb, const Signature* ptr, const char* symbol = nullptr)
1582  {
1583  this->addSignature<Signature, SRet>(cb, symbol);
1584  this->addSignature<Signature, SRet>(ptr, symbol);
1585  return *this;
1586  }
1587 
1588  // Native Binding
1589  template <typename Signature, bool SRet = false>
1590  inline FunctionBuilder&
1591  addSignature(const IRFunctionBase::GeneratorNativeCb& cb, const char* symbol = nullptr)
1592  {
1594  using IRFType = typename std::conditional
1596  OPENVDB_ASSERT(!(SRet && mCurrentSettings->mReadOnly));
1597  const std::string s = symbol ? symbol : this->genSymbol<Signature>();
1598  this->addSignatureImpl<IRFType>(s, cb);
1599  return *this;
1600  }
1601 
1602  // Native Binding
1603  template <typename Signature, bool SRet = false>
1604  inline FunctionBuilder&
1605  addSignature(const IRFunctionBase::GeneratorNativeCb& cb, const Signature* ptr, const char* symbol = nullptr)
1606  {
1608  this->addSignature<Signature, SRet>(cb, symbol);
1609  this->addSignature<Signature, SRet>(ptr, symbol);
1610  return *this;
1611  }
1612 
1613  // Native Binding
1614  template <typename Signature, bool SRet = false>
1615  inline FunctionBuilder&
1617  const char* symbol = nullptr)
1618  {
1619  using IRFType = typename std::conditional
1621  OPENVDB_ASSERT(!(SRet && mCurrentSettings->mReadOnly));
1622  const std::string s = symbol ? symbol : this->genSymbol<Signature>();
1623  this->addSignatureImpl<IRFType>(s, cb);
1624  return *this;
1625  }
1626 
1627  // Native Binding
1628  template <typename Signature, bool SRet = false>
1629  inline FunctionBuilder&
1630  addSignature(const IRFunctionBase::GeneratorArgumentsCb& cb, const Signature* ptr, const char* symbol = nullptr)
1631  {
1632  this->addSignature<Signature, SRet>(cb, symbol);
1633  this->addSignature<Signature, SRet>(ptr, symbol);
1634  return *this;
1635  }
1636 
1637  inline FunctionBuilder& addDependency(const char* name)
1638  {
1639  mCurrentSettings->mDeps.emplace_back(name); return *this;
1640  }
1641 
1642  inline FunctionBuilder& setEmbedIR(bool on) { mCurrentSettings->mEmbedIR = on; return *this; }
1643  inline FunctionBuilder& setConstantFold(bool on) { mCurrentSettings->mConstantFold = on; return *this; }
1644  inline FunctionBuilder& setArgumentNames(const std::vector<const char*>& names)
1645  {
1646  mCurrentSettings->mNames = std::make_unique<SmallArgumentVector<const char*>>();
1647  mCurrentSettings->mNames->assign(names.begin(), names.end());
1648  return *this;
1649  }
1650 
1651  /// @brief Parameter and Function Attributes. When designing a C binding,
1652  /// llvm will be unable to assign parameter markings to the return
1653  /// type, function body or parameter attributes due to there not
1654  /// being any visibility on the function itself during codegen.
1655  /// The best way to ensure performant C bindings is to ensure
1656  /// that the function is marked with the required llvm parameters.
1657  /// @note Some of the most common are shown in FunctionBuilder::setBuiltin,
1658  /// but also consider FunctionBuilder::setReadOnly
1659  inline FunctionBuilder&
1660  addParameterAttribute(const size_t idx, const llvm::Attribute::AttrKind attr)
1661  {
1662  mCurrentSettings->mParamAttrs[idx].emplace_back(attr);
1663  return *this;
1664  }
1665 
1666  inline FunctionBuilder&
1667  addReturnAttribute(const llvm::Attribute::AttrKind attr)
1668  {
1669  mCurrentSettings->mRetAttrs.emplace_back(attr);
1670  return *this;
1671  }
1672 
1673  inline FunctionBuilder&
1674  addFunctionAttribute(const llvm::Attribute::AttrKind attr)
1675  {
1676  mCurrentSettings->mFnAttrs.emplace_back(attr);
1677  return *this;
1678  }
1679 
1680  /// @brief Mark functions currently sharing settings with as "readonly".
1681  /// This enables the strictest possible memory effects for this function
1682  /// in llvm and implies that the function does not write to any memory
1683  /// (i.e. CreateStore) and only reads memory directly from its function
1684  /// arguments (i.e. nothing external to the function). Functions marked
1685  /// as "readonly" but do not adhere to the above will cause UB.
1686  /// @note Obviously invalid for SRET functions!
1687  inline FunctionBuilder&
1688  setReadOnly(const bool on)
1689  {
1690  mCurrentSettings->mReadOnly = on;
1691  return *this;
1692  }
1693 
1694  /// @brief Mark functions currently sharing settings as builtin AX methods.
1695  /// At compile time, this causes the IR function body, arguments and
1696  /// return value to be marked with a set of default attributes that
1697  /// apply to all builtin methods, as they are expected to only be called
1698  /// by AX programs. Currently this results in:
1699  ///
1700  /// Function Attributes:
1701  /// llvm::Attribute::NoFree
1702  /// llvm::Attribute::WillReturn
1703  /// llvm::Attribute::NoRecurse
1704  /// llvm::Attribute::NoUnwind
1705  /// llvm::Attribute::AlwaysInline
1706  /// Return Attributes: -
1707  /// Parameter Attributes (Ptrs):
1708  /// llvm::Attribute::NonNull
1709  /// llvm::Attribute::NoUndef
1710  /// llvm::Attribute::NoFree
1711  /// llvm::Attribute::NoCapture / (llvm::CaptureInfo::none() from LLVM 21)
1712  ///
1713  /// @warning Attributes in this method may be extended - as such, you
1714  /// should mark external function with individual attributes instead of
1715  /// calling this.
1716  inline FunctionBuilder&
1718  {
1719  // note that we have to defer the attribute setting to compile time as
1720  // various attributes need to know the function types. Would be nice if
1721  // we could do it all in the builder.
1722  mCurrentSettings->mType = Function::Type::kBuiltin;
1723  return *this;
1724  }
1725 
1726  /// @brief Mark this function as an external kernel entry point. Should
1727  /// only be used by the ComputeGenerators. Results in a number of function
1728  /// attributes/parameters being set.
1729  inline FunctionBuilder&
1731  {
1732  mCurrentSettings->mType = Function::Type::kKernel;
1733  return *this;
1734  }
1735 
1736  inline FunctionBuilder& setDocumentation(const char* doc)
1737  {
1738  mDoc = doc;
1739  return *this;
1740  }
1741 
1743  {
1744  mDeclPref = pref;
1745  return *this;
1746  }
1747 
1748  inline FunctionGroup::UniquePtr get() const
1749  {
1750  for (auto& decl : mCFunctions)
1751  {
1752  const auto& s = mSettings.at(decl.get());
1753  if (s->mNames) decl->mNames = *s->mNames;
1754  decl->mDeps = s->mDeps;
1755  decl->setConstantFold(s->mConstantFold);
1756  if (!s->mFnAttrs.empty()) decl->attrs().mFnAttrs = s->mFnAttrs;
1757  if (!s->mRetAttrs.empty()) decl->attrs().mRetAttrs = s->mRetAttrs;
1758  if (!s->mParamAttrs.empty()) {
1759  for (auto& idxAttrs : s->mParamAttrs) {
1760  if (idxAttrs.first > decl->size()) continue;
1761  decl->attrs().mParamAttrs[idxAttrs.first] = idxAttrs.second;
1762  }
1763  }
1764  if (s->mReadOnly) decl->attrs().mReadOnly = true;
1765  if (s->mType != Function::Type::kNormal) {
1766  decl->attrs().mType = s->mType;
1767  }
1768  }
1769 
1770  for (auto& decl : mIRFunctions)
1771  {
1772  const auto& s = mSettings.at(decl.get());
1773  if (s->mNames) decl->mNames = *s->mNames;
1774  decl->mDeps = s->mDeps;
1775  decl->setEmbedIR(s->mEmbedIR);
1776  if (!s->mFnAttrs.empty()) decl->attrs().mFnAttrs = s->mFnAttrs;
1777  if (!s->mRetAttrs.empty()) decl->attrs().mRetAttrs = s->mRetAttrs;
1778  if (!s->mParamAttrs.empty()) {
1779  for (auto& idxAttrs : s->mParamAttrs) {
1780  if (idxAttrs.first > decl->size()) continue;
1781  decl->attrs().mParamAttrs[idxAttrs.first] = idxAttrs.second;
1782  }
1783  }
1784  if (s->mReadOnly) decl->attrs().mReadOnly = true;
1785  if (s->mType != Function::Type::kNormal) {
1786  decl->attrs().mType = s->mType;
1787  }
1788  }
1789 
1790  FunctionGroup::FunctionList functions;
1791 
1792  if (mDeclPref == DeclPreferrence::IR) {
1793  functions.insert(functions.end(), mIRFunctions.begin(), mIRFunctions.end());
1794  }
1795  if (mDeclPref == DeclPreferrence::C) {
1796  functions.insert(functions.end(), mCFunctions.begin(), mCFunctions.end());
1797  }
1798  if (functions.empty()) {
1799  functions.insert(functions.end(), mIRFunctions.begin(), mIRFunctions.end());
1800  functions.insert(functions.end(), mCFunctions.begin(), mCFunctions.end());
1801  }
1802 
1803  return std::make_unique<FunctionGroup>(mName, mDoc, std::move(functions));
1804  }
1805 
1806 private:
1807  template <typename FunctionT, typename...Args>
1808  inline FunctionBuilder& addSignatureImpl(Args&& ...args)
1809  {
1810  Settings::Ptr settings = mCurrentSettings;
1811  if (!mCurrentSettings->isDefault()) {
1812  settings = std::make_shared<Settings>();
1813  }
1814  auto ptr = std::make_shared<FunctionT>(std::move(args)...);
1815  if constexpr (std::is_base_of_v<IRFunctionBase, FunctionT>) {
1816  mIRFunctions.emplace_back(ptr);
1817  }
1818  else {
1819  static_assert(std::is_base_of_v<CFunctionBase, FunctionT>);
1820  mCFunctions.emplace_back(ptr);
1821  }
1822  mSettings[ptr.get()] = settings;
1823  mCurrentSettings = settings;
1824  return *this;
1825  }
1826 
1827  template <typename Signature>
1828  std::string genSymbol() const
1829  {
1830  using Traits = FunctionTraits<Signature>;
1831 
1832  std::string args;
1833  auto callback = [&args](auto type) {
1834  using Type = decltype(type);
1835  args += TypeToSymbol<Type>::s();
1836  };
1837 
1838  ArgumentIterator<Signature>::apply(callback, /*forwards*/true);
1839  /// @note important to prefix all symbols with "ax." so that
1840  /// they will never conflict with internal llvm symbol
1841  /// names (such as standard library methods e.g, cos, cosh
1842 
1843  // assemble the symbol
1844  return "ax." + std::string(this->mName) + "." +
1846  }
1847 
1848 private:
1849  const char* mName = "";
1850  const char* mDoc = "";
1851  DeclPreferrence mDeclPref = IR;
1852  std::vector<CFunctionBase::Ptr> mCFunctions = {};
1853  std::vector<IRFunctionBase::Ptr> mIRFunctions = {};
1854  std::map<const Function*, Settings::Ptr> mSettings = {};
1855  Settings::Ptr mCurrentSettings = nullptr;
1856 };
1857 
1858 } // namespace codegen
1859 } // namespace ax
1860 } // namespace OPENVDB_VERSION_NAME
1861 } // namespace openvdb
1862 
1863 #endif // OPENVDB_AX_CODEGEN_FUNCTION_TYPES_HAS_BEEN_INCLUDED
1864 
Wrapper struct to represent "native" function arguments; that is, the set of Value type that the AX g...
Definition: FunctionTypes.h:348
CFunction(const std::string &symbol, SignatureT *function)
Definition: FunctionTypes.h:1130
std::vector< Function::Ptr > FunctionList
Definition: FunctionTypes.h:1399
Templated interface class for SRET functions. This struct provides the interface for functions that w...
Definition: FunctionTypes.h:919
static std::string s()
Definition: FunctionTypes.h:153
ArgInfoVector(const std::initializer_list< ArgInfo > &info)
Definition: FunctionTypes.h:285
LLVM type mapping from pod types.
Definition: Types.h:67
auto begin()
Definition: FunctionTypes.h:295
FunctionBuilder & setConstantFold(bool on)
Definition: FunctionTypes.h:1643
bool operator==(const ArgInfoVector &other) const
Definition: FunctionTypes.h:292
Definition: FunctionRegistry.h:23
FunctionBuilder & addSignature(const IRFunctionBase::GeneratorCb &cb, const char *symbol=nullptr)
Definition: FunctionTypes.h:1567
static std::string s()
Definition: FunctionTypes.h:157
#define OPENVDB_DEPRECATED
Definition: Platform.h:170
OPENVDB_AX_API void print(const ast::Node &node, const bool numberStatements=true, std::ostream &os=std::cout, const char *indent=" ")
Writes a descriptive printout of a Node hierarchy into a target stream.
ArgInfo types(ArgInfoVector &args, llvm::LLVMContext &C) const override
Overide the ArgInfo type method. This does NOT change the arg order, it simply marks the first argume...
Definition: FunctionTypes.h:950
DeclPreferrence
Definition: FunctionTypes.h:1517
const ArgInfo & GetArgInfo(size_t pos) const
Definition: FunctionTypes.h:427
llvm::FunctionType * llvmFunctionTypeFromSignature(llvm::LLVMContext &C)
Generate an LLVM FunctionType from a function signature.
Definition: FunctionTypes.h:554
NativeArguments(const std::vector< Value > &args)
Definition: FunctionTypes.h:353
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
FunctionBuilder & addSignature(const IRFunctionBase::GeneratorCb &cb, const Signature *ptr, const char *symbol=nullptr)
Definition: FunctionTypes.h:1581
ArgInfo(llvm::Type *utype)
Definition: FunctionTypes.h:228
bool IsPtr() const
Definition: FunctionTypes.h:254
The base/abstract definition for an IR function.
Definition: FunctionTypes.h:1203
typename FunctionTraits< SignatureT >::template Arg< I-1 > ArgT
Definition: FunctionTypes.h:190
auto end() const
Definition: FunctionTypes.h:298
ArgInfo & operator[](size_t pos)
Definition: FunctionTypes.h:316
ArgInfo llvmArgTypesFromSignature(llvm::LLVMContext &C, ArgInfoVector *types=nullptr)
Populate a vector of ArgInfos from a function signature declaration.
Definition: FunctionTypes.h:506
Function::SignatureMatch match(const std::vector< llvm::Type * > &args, llvm::LLVMContext &C) const override
Definition: FunctionTypes.h:1035
bool hasConstantFold() const
Definition: FunctionTypes.h:1081
Function::SignatureMatch match(const ArgInfoVector &args, llvm::LLVMContext &C) const override
Override of match which inserts the SRET type such that the base class methods ignore it...
Definition: FunctionTypes.h:963
#define OPENVDB_ASSERT_MESSAGE(X, MSG)
Definition: Assert.h:42
CFunctionSRet(const std::string &symbol, const SignatureT function)
Definition: FunctionTypes.h:1364
auto & front()
Definition: FunctionTypes.h:303
Value call(const Arguments &args, llvm::IRBuilder<> &B) const override
Definition: FunctionTypes.h:1155
std::function< Value(const Arguments &, llvm::IRBuilder<> &)> GeneratorArgumentsCb
Definition: FunctionTypes.h:1221
const llvm::ArrayRef< llvm::Value * > AsLLVMValues() const
Definition: FunctionTypes.h:426
FunctionBuilder(const char *name)
Definition: FunctionTypes.h:1548
#define OPENVDB_AX_API
Definition: Platform.h:312
static std::string s()
Definition: FunctionTypes.h:164
bool mEmbedIR
Definition: FunctionTypes.h:1327
static std::string s()
Definition: FunctionTypes.h:159
std::shared_ptr< FunctionGroup > Ptr
Definition: FunctionTypes.h:1397
IRFunctionSRet(const std::string &symbol, const IRFunctionBase::GeneratorNativeCb &gen)
Definition: FunctionTypes.h:1378
auto & front() const
Definition: FunctionTypes.h:304
bool operator==(const Vec3< T0 > &v0, const Vec3< T1 > &v1)
Equality operator, does exact floating point comparisons.
Definition: Vec3.h:474
auto rbegin()
Definition: FunctionTypes.h:299
Consolidated llvm types for most supported types.
FunctionBuilder & addFunctionAttribute(const llvm::Attribute::AttrKind attr)
Definition: FunctionTypes.h:1674
auto rbegin() const
Definition: FunctionTypes.h:301
static void apply(const OpT &, const bool)
Definition: FunctionTypes.h:217
Definition: Coord.h:590
Type[SIZE] ArrayType
Definition: FunctionTypes.h:120
bool operator!=(const ArgInfo &other) const
Definition: FunctionTypes.h:247
T Type
Definition: FunctionTypes.h:118
std::function< llvm::Value *(const std::vector< llvm::Value * > &, llvm::IRBuilder<> &)> GeneratorCb
Legacy callback, will eventually be deprecated in favour of using the GeneratorArgumentsCb.
Definition: FunctionTypes.h:1227
llvm::Type * types(std::vector< llvm::Type * > &types, llvm::LLVMContext &C) const override
Definition: FunctionTypes.h:1351
Represents a concrete C function binding with the first argument as its return type.
Definition: FunctionTypes.h:1361
llvm::Value * insertStaticAlloca(llvm::IRBuilder<> &B, llvm::Type *type, llvm::Value *size=nullptr)
Insert a stack allocation at the beginning of the current function of the provided type and size...
Definition: Utils.h:117
Definition: Exceptions.h:38
static std::string s()
Definition: FunctionTypes.h:151
static std::string s()
Definition: FunctionTypes.h:154
llvm::SmallVector< T, 3 > SmallArgumentVector
Typedef a stack allocated array with malloc grow support for anything which is relatively small and b...
Definition: FunctionTypes.h:110
bool operator!=(const ArgInfoVector &other) const
Definition: FunctionTypes.h:293
std::shared_ptr< Function > Ptr
Definition: FunctionTypes.h:604
NativeArguments AsNativeValues() const
Definition: FunctionTypes.h:416
IRFunctionSRet(const std::string &symbol, const IRFunctionBase::GeneratorCb &gen)
Definition: FunctionTypes.h:1375
const Value & operator[](size_t pos) const
Definition: FunctionTypes.h:365
void PrependArg(const Value &val)
Definition: FunctionTypes.h:454
auto empty() const
Definition: FunctionTypes.h:310
Represents a concrete IR function with the first argument as its return type.
Definition: FunctionTypes.h:1372
bool isDefault() const
Definition: FunctionTypes.h:1525
Definition: FunctionTypes.h:1518
FunctionBuilder & setEmbedIR(bool on)
Definition: FunctionTypes.h:1642
llvm::Value * call(const std::vector< llvm::Value * > &args, llvm::IRBuilder<> &B) const override
Override of call which allocates the required SRET llvm::Value for this function. ...
Definition: FunctionTypes.h:985
FunctionBuilder & addDependency(const char *name)
Definition: FunctionTypes.h:1637
const SmallArgumentVector< const char * > & dependencies() const
Definition: FunctionTypes.h:794
const char * name() const
Definition: FunctionTypes.h:1459
llvm::Type * llvmTypesFromSignature(llvm::LLVMContext &C, std::vector< llvm::Type * > *types=nullptr)
Populate a vector of llvm types from a function signature declaration.
Definition: FunctionTypes.h:479
static std::string s()
Definition: FunctionTypes.h:155
size_t size() const
Definition: FunctionTypes.h:400
FunctionBuilder & addSignature(const Signature *ptr, const char *symbol=nullptr)
Definition: FunctionTypes.h:1555
std::function< Value(const NativeArguments &, llvm::IRBuilder<> &)> GeneratorNativeCb
The IR callback function which will write the LLVM IR for this function&#39;s body.
Definition: FunctionTypes.h:1218
std::unique_ptr< FunctionGroup > UniquePtr
Definition: FunctionTypes.h:1398
auto begin() const
Definition: FunctionTypes.h:297
auto pop_back()
Definition: FunctionTypes.h:307
static std::string s()
Definition: FunctionTypes.h:149
Templated argument iterator which implements various small functions per argument type...
Definition: FunctionTypes.h:188
auto & back()
Definition: FunctionTypes.h:305
IRFunctionSRet(const std::string &symbol, const IRFunctionBase::GeneratorArgumentsCb &gen)
Definition: FunctionTypes.h:1381
Definition: FunctionTypes.h:727
ArrayType mmArgs
Definition: FunctionTypes.h:121
OPENVDB_AX_API NativeArguments Cast(const Function &F, llvm::IRBuilder<> &B) const
Cast these arguments to match the given function&#39;s signature.
const ArgInfo & operator[](size_t pos) const
Definition: FunctionTypes.h:321
void setConstantFold(bool on)
Definition: FunctionTypes.h:1080
const char * symbol() const
The function symbol name.
Definition: FunctionTypes.h:766
llvm::Value * call(const std::vector< llvm::Value * > &args, llvm::IRBuilder<> &B, const bool cast) const override
Definition: FunctionTypes.h:1049
const char * argName(const size_t idx) const
Returns the descriptive name of the given argument index.
Definition: FunctionTypes.h:773
auto size() const
Definition: FunctionTypes.h:309
IRFunction(const std::string &symbol, const GeneratorArgumentsCb &gen)
Definition: FunctionTypes.h:1341
auto erase(ContainerT::const_iterator iter)
Definition: FunctionTypes.h:311
Value & operator[](size_t pos)
Definition: FunctionTypes.h:360
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
Arbitrary, potentially "non-native" arguments. This wrapper struct can be used when generating functi...
Definition: FunctionTypes.h:381
Represents a concrete IR function.
Definition: FunctionTypes.h:1332
llvm::Type * GetUnderlyingType() const
Definition: FunctionTypes.h:259
The base class for all C bindings.
Definition: FunctionTypes.h:1070
bool operator==(const ArgInfo &other) const
Definition: FunctionTypes.h:240
void SetIsReturn()
Definition: FunctionTypes.h:268
FunctionBuilder & setDocumentation(const char *doc)
Definition: FunctionTypes.h:1736
typename ArgT::Type ArgumentValueType
Definition: FunctionTypes.h:191
IRFunctionBase(const std::string &symbol, const GeneratorArgumentsCb &gen, const size_t size)
Definition: FunctionTypes.h:1318
An extremely basic but native representation of a string class with SSO support. This exists to provi...
Definition: String.h:33
virtual llvm::Value * fold(const std::vector< llvm::Value * > &, llvm::LLVMContext &) const
Definition: FunctionTypes.h:1085
Constant folding support structure.
Definition: ConstantFolding.h:35
IRFunction(const std::string &symbol, const GeneratorCb &gen)
Definition: FunctionTypes.h:1337
Object to array conversion methods to allow functions to return vector types. These containers provid...
Definition: FunctionTypes.h:117
Alias mapping between two types, a frontend type T1 and a backend type T2. This class is the intended...
Definition: Types.h:268
llvm::Value * operator[](size_t pos) const
Definition: FunctionTypes.h:430
static std::string s()
Definition: FunctionTypes.h:148
static std::string s()
Definition: FunctionTypes.h:150
FunctionBuilder & addParameterAttribute(const size_t idx, const llvm::Attribute::AttrKind attr)
Parameter and Function Attributes. When designing a C binding, llvm will be unable to assign paramete...
Definition: FunctionTypes.h:1660
uint8_t NumPtrs() const
Definition: FunctionTypes.h:255
Represents a concrete C function binding.
Definition: FunctionTypes.h:1116
auto clear()
Definition: FunctionTypes.h:308
Definition: Exceptions.h:13
void print(llvm::LLVMContext &C, std::ostream &os, const char *name=nullptr, const bool axTypes=true) const override
Override of print to avoid printing out the SRET type.
Definition: FunctionTypes.h:1008
A group of functions which all have the same name but different signatures. For example: float abs(fl...
Definition: FunctionTypes.h:1395
Intermediate representation wrapper for supported value types in AX as immutable instances.
Definition: Value.h:62
auto rend() const
Definition: FunctionTypes.h:302
size_t size() const
The number of arguments that this function has.
Definition: FunctionTypes.h:762
static std::string s()
Definition: FunctionTypes.h:152
llvm::Value * GetValue() const
Access the underlying llvm Value.
Definition: Value.h:673
IRFunctionBase(const std::string &symbol, const GeneratorNativeCb &gen, const size_t size)
Definition: FunctionTypes.h:1309
const FunctionList & list() const
Accessor to the underlying function signature list.
Definition: FunctionTypes.h:1458
bool IsReturn() const
Definition: FunctionTypes.h:258
void reserve(size_t i)
Definition: FunctionTypes.h:313
FunctionBuilder & addSignature(const IRFunctionBase::GeneratorArgumentsCb &cb, const char *symbol=nullptr)
Definition: FunctionTypes.h:1616
FunctionBuilder & setExternalKernel()
Mark this function as an external kernel entry point. Should only be used by the ComputeGenerators. Results in a number of function attributes/parameters being set.
Definition: FunctionTypes.h:1730
OutGridT XformOp & op
Definition: ValueTransformer.h:139
FunctionBuilder & setReadOnly(const bool on)
Mark functions currently sharing settings with as "readonly". This enables the strictest possible mem...
Definition: FunctionTypes.h:1688
void setEmbedIR(bool on)
Enable or disable the embedding of IR. Embedded IR is currently required for function which use paren...
Definition: FunctionTypes.h:1231
ArgInfo types(ArgInfoVector &types, llvm::LLVMContext &C) const override
Populate a vector of ArgInfos which describe this function signature. This method is used by Function...
Definition: FunctionTypes.h:1136
SignatureMatch
The result type from calls to Function::match.
Definition: FunctionTypes.h:724
auto & back() const
Definition: FunctionTypes.h:306
llvm::Value * call(const std::vector< llvm::Value * > &args, llvm::IRBuilder<> &B, const bool cast) const override
Definition: FunctionTypes.h:1188
FunctionBuilder & addSignature(const IRFunctionBase::GeneratorNativeCb &cb, const char *symbol=nullptr)
Definition: FunctionTypes.h:1591
static std::string s()
Definition: FunctionTypes.h:156
llvm::Constant * fold(const std::vector< llvm::Value * > &args, llvm::LLVMContext &C) const override final
Definition: FunctionTypes.h:1163
llvm::Type * GetType() const
Definition: FunctionTypes.h:260
SmallArgumentVector< ArgInfo > ContainerT
Definition: FunctionTypes.h:282
size_t size() const
Definition: FunctionTypes.h:359
const GeneratorArgumentsCb mGen
Definition: FunctionTypes.h:1326
bool IsVoid() const
Definition: FunctionTypes.h:257
Metadata associated with a function argument or return value.
Definition: FunctionTypes.h:224
void AddArg(llvm::Value *val, const ArgInfo &type)
Definition: FunctionTypes.h:436
static std::string s()
Definition: FunctionTypes.h:147
bool AreNativeValues() const
Definition: FunctionTypes.h:402
llvm::Type * types(std::vector< llvm::Type * > &types, llvm::LLVMContext &C) const override
Definition: FunctionTypes.h:1179
ArgInfo(const Value &val)
Definition: FunctionTypes.h:226
Constant folding for C++ bindings.
auto rend()
Definition: FunctionTypes.h:300
void verifyResultType(const llvm::Type *result, const llvm::Type *expected) const
Definition: FunctionTypes.h:1273
IRFunctionBase(const std::string &symbol, const GeneratorCb &gen, const size_t size)
Definition: FunctionTypes.h:1283
FunctionBuilder & addSignature(const IRFunctionBase::GeneratorArgumentsCb &cb, const Signature *ptr, const char *symbol=nullptr)
Definition: FunctionTypes.h:1630
IRFunction(const std::string &symbol, const GeneratorNativeCb &gen)
Definition: FunctionTypes.h:1339
OPENVDB_AX_API void printSignature(std::ostream &os, const std::vector< llvm::Type * > &types, const llvm::Type *returnType, const char *name=nullptr, const std::vector< const char * > &names={}, const bool axTypes=false)
uint64_t address() const override final
Returns the global address of this function.
Definition: FunctionTypes.h:1141
Intermediate representation of supported AX values.
llvm::Function * create(llvm::Module &M) const
Convenience method which always uses the provided module to find the function or insert it if necessa...
Definition: FunctionTypes.h:667
ArgInfo(llvm::Type *utype, uint8_t ptrs, bool ret=false)
Definition: FunctionTypes.h:229
Templated function traits which provides compile-time index access to the types of the function signa...
Definition: Types.h:311
static std::string s()
Definition: FunctionTypes.h:160
FunctionGroup(const char *name, const char *doc, const FunctionList &list)
Definition: FunctionTypes.h:1401
SRetFunction(Args &&...ts)
Forward all arguments to the derived class.
Definition: FunctionTypes.h:1066
Value AsNativeValue(const size_t i) const
Definition: FunctionTypes.h:410
Utility code generation methods for performing various llvm operations.
ArgInfo types(ArgInfoVector &types, llvm::LLVMContext &C) const override
Populate a vector of ArgInfos which describe this function signature. This method is used by Function...
Definition: FunctionTypes.h:1344
Value call(const NativeArguments &args, llvm::IRBuilder<> &B) const
Definition: FunctionTypes.h:716
FunctionBuilder & setArgumentNames(const std::vector< const char * > &names)
Definition: FunctionTypes.h:1644
NativeArguments(const std::initializer_list< Value > &args)
Definition: FunctionTypes.h:351
Function(const size_t size, const std::string &symbol)
Definition: FunctionTypes.h:606
Type to symbol conversions - these characters are used to build each functions unique signature...
Definition: FunctionTypes.h:147
FunctionBuilder & setBuiltin()
Mark functions currently sharing settings as builtin AX methods. At compile time, this causes the IR ...
Definition: FunctionTypes.h:1717
Value call(const Arguments &args, llvm::IRBuilder<> &B) const override
Definition: FunctionTypes.h:997
void llvmTypeToString(const llvm::Type *const type, std::string &str)
Prints an llvm type to a std string.
Definition: Utils.h:80
llvm::Value * call(const std::vector< llvm::Value * > &args, llvm::IRBuilder<> &B) const override
Definition: FunctionTypes.h:1147
void AddArg(const Value &val)
Definition: FunctionTypes.h:448
CFunctionBase(const size_t size, const std::string &symbol)
Definition: FunctionTypes.h:1101
const char * doc() const
Definition: FunctionTypes.h:1460
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
const ArgInfoVector & GetArgInfo() const
Definition: FunctionTypes.h:428
The base/abstract representation of an AX function. Derived classes must implement the Function::type...
Definition: FunctionTypes.h:602
void emplace_back(Args &&...args)
Definition: FunctionTypes.h:315
bool hasEmbedIR() const
Definition: FunctionTypes.h:1232
FunctionBuilder & setPreferredImpl(DeclPreferrence pref)
Definition: FunctionTypes.h:1742
Container of ArgInfos. This class makes up part of the Function API for querying signature informatio...
Definition: FunctionTypes.h:279
static std::string s()
Definition: FunctionTypes.h:169
bool IsNative() const
Definition: FunctionTypes.h:256
std::shared_ptr< Settings > Ptr
Definition: FunctionTypes.h:1523
SmallArgumentVector< llvm::Type * > AsLLVMTypes() const
Definition: FunctionTypes.h:327
bool IsMatchingType(const ArgInfo &other) const
Definition: FunctionTypes.h:248
virtual ArgInfo types(ArgInfoVector &, llvm::LLVMContext &) const
Populate a vector of ArgInfos which describe this function signature. This method is used by Function...
Definition: FunctionTypes.h:625
The FunctionBuilder class provides a builder pattern framework to allow easy and valid construction o...
Definition: FunctionTypes.h:1515
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition: Platform.h:171
void AddArg(const Value &val)
Definition: FunctionTypes.h:370
Arguments(const NativeArguments &args)
Definition: FunctionTypes.h:387
FunctionBuilder & addReturnAttribute(const llvm::Attribute::AttrKind attr)
Definition: FunctionTypes.h:1667
static void apply(const OpT &op, const bool forwards)
Definition: FunctionTypes.h:200
auto end()
Definition: FunctionTypes.h:296
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
FunctionBuilder & addSignature(const IRFunctionBase::GeneratorNativeCb &cb, const Signature *ptr, const char *symbol=nullptr)
Definition: FunctionTypes.h:1605
static std::string s()
Definition: FunctionTypes.h:158