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