1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file codegen/ConstantFolding.h
5 ///
6 /// @authors Nick Avramoussis
7 ///
8 /// @brief  Constant folding for C++ bindings.
9 ///
10 
11 #ifndef OPENVDB_AX_CODEGEN_CONSTANT_FOLDING_HAS_BEEN_INCLUDED
12 #define OPENVDB_AX_CODEGEN_CONSTANT_FOLDING_HAS_BEEN_INCLUDED
13 
14 #include "Types.h"
15 
16 #include <openvdb/version.h>
17 
18 #include <llvm/IR/Constants.h>
19 
20 #include <type_traits>
21 #include <vector>
22 
23 namespace openvdb {
24 OPENVDB_USE_VERSION_NAMESPACE
25 namespace OPENVDB_VERSION_NAME {
26 
27 namespace ax {
28 namespace codegen {
29 
30 /// @brief  Constant folding support structure
31 ///
32 template <typename SignatureT,
33     size_t I = FunctionTraits<SignatureT>::N_ARGS>
34 struct ConstantFolder
35 {
36     using ArgT = typename FunctionTraits<SignatureT>::template Arg<I-1>;
37     using ArgumentValueType = typename ArgT::Type;
38 
39     // @brief    Attempts evaluate a given function with a provided set of constant llvm
40     //           values. If successful, the function is invoked and the result is stored
41     //           and returned in an llvm::Value.
42     // @details  Currently only scalar constant folding is supported due to the way
43     //           vectors and matrices are alloced. Functions which return void are also
44     //           not supported for constant folding.
45     // @param args  The vector of llvm constants that comprise the function arguments.
46     //              Note that the size of this vector is expected to match the size of
47     //              the required function arguments and the templated parameter I
48     // @param function  The function to invoke if all arguments have a valid mapping.
49     // @param C   The llvm Context
50     // @param ts  The list of evaluated C types from the provided llvm constants. This
51     //            is expected to be empty (not provided) on the first call to fold and
52     //            is used on subsequent recursive calls.
53     template <typename ...Tys>
54     static llvm::Value*
foldConstantFolder55     fold(const std::vector<llvm::Constant*>& args,
56          const SignatureT& function,
57          llvm::LLVMContext& C,
58          Tys&&... ts)
59     {
60         assert(I-1 < args.size());
61         llvm::Constant* constant = args[I-1];
62         const llvm::Type* type = constant->getType();
63         if (type->isIntegerTy()) {
64             assert(llvm::isa<llvm::ConstantInt>(constant));
65             llvm::ConstantInt* cint =
66                 llvm::cast<llvm::ConstantInt>(constant);
67             const uint64_t val = cint->getLimitedValue();
68             return call<uint64_t, ArgumentValueType>(args, function, C, val, ts...);
69         }
70         else if (type->isFloatTy() || type->isDoubleTy()) {
71             assert(llvm::isa<llvm::ConstantFP>(constant));
72             llvm::ConstantFP* cfp =
73                 llvm::cast<llvm::ConstantFP>(constant);
74             const llvm::APFloat& apf = cfp->getValueAPF();
75             if (type->isFloatTy()) {
76                 const float val = apf.convertToFloat();
77                 return call<float, ArgumentValueType>(args, function, C, val, ts...);
78             }
79             if (type->isDoubleTy()) {
80                 const double val = apf.convertToDouble();
81                 return call<double, ArgumentValueType>(args, function, C, val, ts...);
82             }
83         }
84         else if (type->isArrayTy()) {
85             // @todo currently all arrays are alloced anyway which
86             // needs to be handled or changed
87             return nullptr;
88         }
89         // fallback
90         return nullptr;
91     }
92 private:
93     // @brief  Specialization for supported implicit casting matching AX's supported
94     //         scalar casting. Continues to traverse the constant argument list.
95     template <typename In, typename Out, typename ...Tys>
96     static typename std::enable_if<std::is_convertible<In, Out>::value, llvm::Value*>::type
callConstantFolder97     call(const std::vector<llvm::Constant*>& args,
98          const SignatureT& function,
99          llvm::LLVMContext& C,
100          const In& arg,
101          Tys&&... ts)
102     {
103         using Next = ConstantFolder<SignatureT, I-1>;
104         return Next::fold(args, function, C, Out(arg), ts...);
105     }
106 
107     // @brief  Specialization for unsupported implicit casting. Bails out with a
108     //         nullptr return.
109     template <typename In, typename Out, typename ...Tys>
110     static typename std::enable_if<!std::is_convertible<In, Out>::value, llvm::Value*>::type
callConstantFolder111     call(const std::vector<llvm::Constant*>&,
112          const SignatureT&,
113          llvm::LLVMContext&,
114          const In&, Tys&&...)
115     {
116         return nullptr;
117     }
118 };
119 
120 template <typename SignatureT>
121 struct ConstantFolder<SignatureT, 0>
122 {
123     // @brief  The final call to fold when all arguments have been evaluated (or no
124     //         arguments exist).
125     template <typename ...Tys>
126     static llvm::Value*
127     fold(const std::vector<llvm::Constant*>& args,
128          const SignatureT& function,
129          llvm::LLVMContext& C,
130          Tys&&... ts)
131     {
132         using ReturnT = typename FunctionTraits<SignatureT>::ReturnType;
133         return call<ReturnT>(args, function, C, ts...);
134     }
135 
136 private:
137 
138     // @brief  Specialization for the invoking of the provided function if the return
139     //         type is not void or a pointer
140     template <typename ReturnT, typename ...Tys>
141     static typename std::enable_if<!std::is_pointer<ReturnT>::value &&
142         !std::is_same<ReturnT, void>::value, llvm::Value*>::type
143     call(const std::vector<llvm::Constant*>&,
144          const SignatureT& function,
145          llvm::LLVMContext& C,
146          Tys&&... ts)
147     {
148         const ReturnT result = function(ts...);
149         return LLVMType<ReturnT>::get(C, result);
150     }
151 
152     // @brief  Specialization if the return type is void or a pointer. No folding is
153     //         supported.
154     template <typename ReturnT, typename ...Tys>
155     static typename std::enable_if<std::is_pointer<ReturnT>::value ||
156         std::is_same<ReturnT, void>::value, llvm::Value*>::type
157     call(const std::vector<llvm::Constant*>&,
158          const SignatureT&,
159          llvm::LLVMContext&,
160          Tys&&...)
161     {
162         return nullptr;
163     }
164 };
165 
166 } // namespace codegen
167 } // namespace ax
168 } // namespace OPENVDB_VERSION_NAME
169 } // namespace openvdb
170 
171 #endif // OPENVDB_AX_CODEGEN_CONSTANT_FOLDING_HAS_BEEN_INCLUDED
172 
173