Line | Branch | Exec | Source |
---|---|---|---|
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* | ||
55 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 797 times.
|
1594 | fold(const std::vector<llvm::Constant*>& args, |
56 | const SignatureT& function, | ||
57 | llvm::LLVMContext& C, | ||
58 | Tys&&... ts) | ||
59 | { | ||
60 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 797 times.
|
1594 | assert(I-1 < args.size()); |
61 |
2/2✓ Branch 0 taken 132 times.
✓ Branch 1 taken 665 times.
|
1594 | llvm::Constant* constant = args[I-1]; |
62 | const llvm::Type* type = constant->getType(); | ||
63 |
2/2✓ Branch 0 taken 132 times.
✓ Branch 1 taken 665 times.
|
1594 | if (type->isIntegerTy()) { |
64 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
|
264 | assert(llvm::isa<llvm::ConstantInt>(constant)); |
65 | llvm::ConstantInt* cint = | ||
66 | 264 | 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 |
4/4✓ Branch 0 taken 436 times.
✓ Branch 1 taken 229 times.
✓ Branch 2 taken 360 times.
✓ Branch 3 taken 76 times.
|
1330 | else if (type->isFloatTy() || type->isDoubleTy()) { |
71 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 589 times.
|
1178 | assert(llvm::isa<llvm::ConstantFP>(constant)); |
72 | llvm::ConstantFP* cfp = | ||
73 | 1178 | llvm::cast<llvm::ConstantFP>(constant); | |
74 | const llvm::APFloat& apf = cfp->getValueAPF(); | ||
75 |
2/2✓ Branch 0 taken 229 times.
✓ Branch 1 taken 360 times.
|
1178 | if (type->isFloatTy()) { |
76 | 458 | const float val = apf.convertToFloat(); | |
77 | return call<float, ArgumentValueType>(args, function, C, val, ts...); | ||
78 | } | ||
79 |
1/2✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
|
720 | if (type->isDoubleTy()) { |
80 | 720 | 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 | ||
97 | 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 | 280 | 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 | ||
111 | 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 |
1/6✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
|
442 | const ReturnT result = function(ts...); |
149 |
1/6✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
|
442 | 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 |