OpenVDB  12.1.0
Utils.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/Utils.h
5 ///
6 /// @authors Nick Avramoussis
7 ///
8 /// @brief Utility code generation methods for performing various llvm
9 /// operations
10 ///
11 
12 #ifndef OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
13 #define OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
14 
15 #include "Types.h"
16 
17 #include "../ast/Tokens.h"
18 #include "../Exceptions.h"
19 
20 #include <openvdb/version.h>
21 #include <openvdb/util/Assert.h>
22 
23 #include <llvm/IR/IRBuilder.h>
24 #include <llvm/IR/LLVMContext.h>
25 #include <llvm/Support/raw_ostream.h> // llvm::errs()
26 
27 namespace openvdb {
29 namespace OPENVDB_VERSION_NAME {
30 
31 namespace ax {
32 namespace codegen {
33 
34 /// @note Function definitions for some types returned from automatic token to
35 /// llvm IR operations. See llvmArithmeticConversion and llvmBianryConversion
36 
37 using CastFunction = std::function<llvm::Value*
38  (llvm::IRBuilder<>&, llvm::Value*, llvm::Type*)>;
39 
40 using BinaryFunction = std::function<llvm::Value*
41  (llvm::IRBuilder<>&, llvm::Value*, llvm::Value*)>;
42 
43 inline bool AssertOpaquePtrs([[maybe_unused]]llvm::Value* opaque, [[maybe_unused]]llvm::Type* type)
44 {
45 #if LLVM_VERSION_MAJOR <= 15
46  if (opaque->getType()->getPointerElementType() != type) {
47 #ifdef OPENVDB_ENABLE_ASSERTS
48  llvm::errs() << "Opaque ptr check failed:\n";
49  llvm::errs() << "Input Value: "; opaque->print(llvm::errs()); llvm::errs() << "\n";
50  llvm::errs() << "Input Ptr Type: "; opaque->getType()->getPointerElementType()->print(llvm::errs()); llvm::errs() << "\n";
51  llvm::errs() << "Type: "; type->print(llvm::errs()); llvm::errs() << "\n";
52 #endif
53  return false;
54  }
55 #endif
56  return true;
57 }
58 
59 /// @brief Populate a vector of llvm Types from a vector of llvm values
60 ///
61 /// @param values A vector of llvm values to retrieve types from
62 /// @param types A vector of llvm types to populate
63 ///
64 inline void
65 valuesToTypes(const std::vector<llvm::Value*>& values,
66  std::vector<llvm::Type*>& types)
67 {
68  types.reserve(values.size());
69  for (const auto& v : values) {
70  types.emplace_back(v->getType());
71  }
72 }
73 
74 /// @brief Prints an llvm type to a std string
75 ///
76 /// @param type The llvm type to convert
77 /// @param str The string to store the type info to
78 ///
79 inline void
80 llvmTypeToString(const llvm::Type* const type, std::string& str)
81 {
82  llvm::raw_string_ostream os(str);
83  type->print(os);
84  os.flush();
85 }
86 
87 /// @brief Return an llvm value representing a pointer to the provided ptr builtin
88 /// ValueT.
89 /// @note This is probably not a suitable solution for anything other than POD
90 /// types and should be used with caution.
91 ///
92 /// @param ptr A pointer to a type of ValueT whose address will be computed and
93 /// returned
94 /// @param builder The current llvm IRBuilder
95 ///
96 template <typename ValueT>
97 inline llvm::Value*
98 llvmPointerFromAddress(const ValueT* const& ptr,
99  llvm::IRBuilder<>& builder)
100 {
101  llvm::Value* address =
102  llvm::ConstantInt::get(llvm::Type::getIntNTy(builder.getContext(), sizeof(uintptr_t)*8),
103  reinterpret_cast<uintptr_t>(ptr));
104  return builder.CreateIntToPtr(address, LLVMType<ValueT*>::get(builder.getContext()));
105 }
106 
107 /// @brief Insert a stack allocation at the beginning of the current function
108 /// of the provided type and size. The IRBuilder's insertion point must
109 /// be set to a BasicBlock with a valid Function parent.
110 /// @note If a size is provided, the size must not depend on any other
111 /// instructions. If it does, invalid LLVM IR will bb generated.
112 ///
113 /// @param B The IRBuilder
114 /// @param type The type to allocate
115 /// @param size Optional count of allocations. If nullptr, runs a single allocation
116 inline llvm::Value*
117 insertStaticAlloca(llvm::IRBuilder<>& B,
118  llvm::Type* type,
119  llvm::Value* size = nullptr)
120 {
121  llvm::StructType* strtype = LLVMType<codegen::String>::get(B.getContext());
122  // Create the allocation at the start of the function block
123  llvm::Function* parent = B.GetInsertBlock()->getParent();
124  OPENVDB_ASSERT(parent && !parent->empty());
125  auto IP = B.saveIP();
126  llvm::BasicBlock& block = parent->front();
127  if (block.empty()) B.SetInsertPoint(&block);
128  else B.SetInsertPoint(&(block.front()));
129  llvm::Value* result = B.CreateAlloca(type, size);
130 
131  /// @note Strings need to be initialised correctly when they are
132  /// created. We alloc them at the start of the function but
133  /// strings in branches may not ever be set to anything. If
134  /// we don't init these correctly, the clearup frees will
135  /// try and free uninitialised memory
136  if (type == strtype) {
137  llvm::Value* cptr = B.CreateStructGEP(strtype, result, 0); // char**
138  llvm::Value* sso = B.CreateStructGEP(strtype, result, 1); // char[]*
139  llvm::Value* sso_load = B.CreateConstGEP2_64(strtype->getTypeAtIndex(1), sso, 0, 0); // char[]
140  llvm::Value* len = B.CreateStructGEP(strtype, result, 2);
141  B.CreateStore(sso_load, cptr); // this->ptr = this->SSO;
142  B.CreateStore(B.getInt64(0), len);
143  }
144  B.restoreIP(IP);
145  return result;
146 }
147 
148 inline llvm::Argument*
149 extractArgument(llvm::Function* F, const size_t idx)
150 {
151  if (!F) return nullptr;
152  if (idx >= F->arg_size()) return nullptr;
153  return llvm::cast<llvm::Argument>(F->arg_begin() + idx);
154 }
155 
156 inline llvm::Argument*
157 extractArgument(llvm::Function* F, const std::string& name)
158 {
159  if (!F) return nullptr;
160  for (auto iter = F->arg_begin(); iter != F->arg_end(); ++iter) {
161  llvm::Argument* arg = llvm::cast<llvm::Argument>(iter);
162  if (arg->getName() == name) return arg;
163  }
164  return nullptr;
165 }
166 
167 /// @brief Returns the highest order type from two LLVM Scalar types
168 ///
169 /// @param typeA The first scalar llvm type
170 /// @param typeB The second scalar llvm type
171 ///
172 inline llvm::Type*
173 typePrecedence(llvm::Type* const typeA,
174  llvm::Type* const typeB)
175 {
176  OPENVDB_ASSERT(typeA && (typeA->isIntegerTy() || typeA->isFloatingPointTy()) &&
177  "First Type in typePrecedence is not a scalar type");
178  OPENVDB_ASSERT(typeB && (typeB->isIntegerTy() || typeB->isFloatingPointTy()) &&
179  "Second Type in typePrecedence is not a scalar type");
180 
181  // handle implicit arithmetic conversion
182  // (http://osr507doc.sco.com/en/tools/clang_conv_implicit.html)
183 
184  if (typeA->isDoubleTy()) return typeA;
185  if (typeB->isDoubleTy()) return typeB;
186 
187  if (typeA->isFloatTy()) return typeA;
188  if (typeB->isFloatTy()) return typeB;
189 
190  if (typeA->isIntegerTy(64)) return typeA;
191  if (typeB->isIntegerTy(64)) return typeB;
192 
193  if (typeA->isIntegerTy(32)) return typeA;
194  if (typeB->isIntegerTy(32)) return typeB;
195 
196  if (typeA->isIntegerTy(16)) return typeA;
197  if (typeB->isIntegerTy(16)) return typeB;
198 
199  if (typeA->isIntegerTy(8)) return typeA;
200  if (typeB->isIntegerTy(8)) return typeB;
201 
202  if (typeA->isIntegerTy(1)) return typeA;
203  if (typeB->isIntegerTy(1)) return typeB;
204 
205  OPENVDB_ASSERT(false && "invalid LLVM type precedence");
206  return nullptr;
207 }
208 
209 /// @brief Returns a CastFunction which represents the corresponding instruction
210 /// to convert a source llvm Type to a target llvm Type. If the conversion
211 /// is unsupported, throws an error.
212 /// @warning This assumes any integer types are signed.
213 /// @param sourceType The source type to cast
214 /// @param targetType The target type to cast to
215 /// @param twine An optional string description of the cast function. This can
216 /// be used for for more verbose llvm information on IR compilation
217 /// failure
218 /// @todo Deprecate this method, move functionality into codegen::Value
219 inline CastFunction
220 llvmArithmeticConversion(const llvm::Type* const sourceType,
221  const llvm::Type* const targetType,
222  const std::string& twine = "")
223 {
224 
225 #define BIND_ARITHMETIC_CAST_OP(Function, Twine) \
226  std::bind(&Function, \
227  std::placeholders::_1, \
228  std::placeholders::_2, \
229  std::placeholders::_3, \
230  Twine)
231 
232 #if LLVM_VERSION_MAJOR >= 19
233 // For Trunc, assume argument is never NUW/NSW (last flags)
234 #define BIND_ARITHMETIC_CAST_OP_TRUNC(Function, Twine) \
235  std::bind(&Function, \
236  std::placeholders::_1, \
237  std::placeholders::_2, \
238  std::placeholders::_3, \
239  Twine, \
240  /*IsNUW*/false, \
241  /*IsNSW*/false)
242 #else
243 #define BIND_ARITHMETIC_CAST_OP_TRUNC(Function, Twine) \
244  std::bind(&Function, \
245  std::placeholders::_1, \
246  std::placeholders::_2, \
247  std::placeholders::_3, \
248  Twine)
249 #endif
250 
251 #if LLVM_VERSION_MAJOR >= 19
252 // For UItoFP, assume argument is never negative (last flag)
253 #define BIND_ARITHMETIC_CAST_OP_UIFP(Function, Twine) \
254  std::bind(&Function, \
255  std::placeholders::_1, \
256  std::placeholders::_2, \
257  std::placeholders::_3, \
258  Twine, \
259  false) // IsNonNeg
260 #else
261 #define BIND_ARITHMETIC_CAST_OP_UIFP(Function, Twine) \
262  std::bind(&Function, \
263  std::placeholders::_1, \
264  std::placeholders::_2, \
265  std::placeholders::_3, \
266  Twine)
267 #endif
268 
269 #if LLVM_VERSION_MAJOR >= 18
270 // For Zext, assume argument is never negative (last flag)
271 #define BIND_ARITHMETIC_CAST_OP_ZEXT(Function, Twine) \
272  std::bind(&Function, \
273  std::placeholders::_1, \
274  std::placeholders::_2, \
275  std::placeholders::_3, \
276  Twine, \
277  false) // IsNonNeg
278 #else
279 #define BIND_ARITHMETIC_CAST_OP_ZEXT(Function, Twine) \
280  std::bind(&Function, \
281  std::placeholders::_1, \
282  std::placeholders::_2, \
283  std::placeholders::_3, \
284  Twine)
285 #endif
286 
287 #if LLVM_VERSION_MAJOR >= 20
288 #define BIND_ARITHMETIC_CAST_OP_FP(Function, Twine) \
289  std::bind(&Function, \
290  std::placeholders::_1, \
291  std::placeholders::_2, \
292  std::placeholders::_3, \
293  Twine, \
294  nullptr) // MDNode *FPMathTag
295 #else
296 #define BIND_ARITHMETIC_CAST_OP_FP(Function, Twine) \
297  std::bind(&Function, \
298  std::placeholders::_1, \
299  std::placeholders::_2, \
300  std::placeholders::_3, \
301  Twine)
302 #endif
303 
304  if (targetType->isDoubleTy()) {
305  if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPExt, twine);
306  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPExt, twine);
307  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
308  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
309  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
310  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
311  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_UIFP(llvm::IRBuilder<>::CreateUIToFP, twine);
312  }
313  else if (targetType->isFloatTy()) {
314  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPTrunc, twine);
315  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPExt, twine);
316  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
317  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
318  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
319  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
320  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_UIFP(llvm::IRBuilder<>::CreateUIToFP, twine);
321  }
322  else if (targetType->isHalfTy()) {
323  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPTrunc, twine);
324  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPTrunc, twine);
325  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
326  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
327  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
328  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
329  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_UIFP(llvm::IRBuilder<>::CreateUIToFP, twine);
330  }
331  else if (targetType->isIntegerTy(64)) {
332  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
333  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
334  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
335  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
336  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
337  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
338  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
339  }
340  else if (targetType->isIntegerTy(32)) {
341  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
342  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
343  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
344  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
345  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
346  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
347  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
348  }
349  else if (targetType->isIntegerTy(16)) {
350  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
351  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
352  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
353  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
354  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
355  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
356  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
357  }
358  else if (targetType->isIntegerTy(8)) {
359  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
360  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
361  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
362  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
363  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
364  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
365  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
366  }
367  else if (targetType->isIntegerTy(1)) {
368  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
369  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
370  else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
371  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
372  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
373  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
374  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
375  }
376 
377 #undef BIND_ARITHMETIC_CAST_OP
378  OPENVDB_ASSERT(false && "invalid LLVM type conversion");
379  return CastFunction();
380 }
381 
382 /// @brief Returns a BinaryFunction representing the corresponding instruction to
383 /// perform on two scalar values, relative to a provided operator token. Note that
384 /// not all operations are supported on floating point types! If the token is not
385 /// supported, or the llvm type is not a scalar type, throws an error.
386 /// @note Various default arguments are bound to provide a simple function call
387 /// signature. For floating point operations, this includes a null pointer to
388 /// the optional metadata node. For integer operations, this includes disabling
389 /// all overflow/rounding optimisations
390 ///
391 /// @param type The type defining the precision of the binary operation
392 /// @param token The token used to create the relative binary operation
393 /// @param twine An optional string description of the binary function. This can
394 /// be used for for more verbose llvm information on IR compilation
395 /// failure
396 inline BinaryFunction
397 llvmBinaryConversion(const llvm::Type* const type,
398  const ast::tokens::OperatorToken& token,
399  const std::string& twine = "")
400 {
401 
402 #define BIND_BINARY_OP(Function) \
403  [twine](llvm::IRBuilder<>& B, llvm::Value* L, llvm::Value* R) \
404  -> llvm::Value* { return B.Function(L, R, twine); }
405 
406  // NOTE: Binary % and / ops always take sign into account (CreateSDiv vs CreateUDiv, CreateSRem vs CreateURem).
407  // See http://stackoverflow.com/questions/5346160/llvm-irbuildercreateudiv-createsdiv-createexactudiv
408  // a%b in AX is implemented as a floored modulo op and is handled explicitly in binaryExpression
409 
410  if (type->isFloatingPointTy()) {
413  && "unable to perform logical or bitwise operation on floating point values");
414 
415  if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateFAdd);
416  else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateFSub);
417  else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateFMul);
418  else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateFDiv);
419  else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateFRem); // Note this is NOT a%b in AX.
420  else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateFCmpOEQ);
421  else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateFCmpONE);
422  else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateFCmpOGT);
423  else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateFCmpOLT);
424  else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOGE);
425  else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOLE);
426  OPENVDB_ASSERT(false && "unrecognised binary operator");
427  }
428  else if (type->isIntegerTy()) {
429  if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateAdd); // No Unsigned/Signed Wrap
430  else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateSub); // No Unsigned/Signed Wrap
431  else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateMul); // No Unsigned/Signed Wrap
432  else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateSDiv); // IsExact = false - when true, poison value if the reuslt is rounded
433  else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateSRem); // Note this is NOT a%b in AX.
434  else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateICmpEQ);
435  else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateICmpNE);
436  else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateICmpSGT);
437  else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateICmpSLT);
438  else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateICmpSGE);
439  else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateICmpSLE);
440  else if (token == ast::tokens::AND) return BIND_BINARY_OP(CreateAnd); // same as BITAND (bool/circuting logic handled a layer up)
441  else if (token == ast::tokens::OR) return BIND_BINARY_OP(CreateOr);
442  else if (token == ast::tokens::SHIFTLEFT) return BIND_BINARY_OP(CreateShl); // No Unsigned/Signed Wrap
443  else if (token == ast::tokens::SHIFTRIGHT) return BIND_BINARY_OP(CreateAShr); // IsExact = false - poison value if any of the bits shifted out are non-zero.
444  else if (token == ast::tokens::BITAND) return BIND_BINARY_OP(CreateAnd); // note same as AND
445  else if (token == ast::tokens::BITOR) return BIND_BINARY_OP(CreateOr);
446  else if (token == ast::tokens::BITXOR) return BIND_BINARY_OP(CreateXor);
447  OPENVDB_ASSERT(false && "unrecognised binary operator");
448  }
449 
450 #undef BIND_BINARY_OP
451  OPENVDB_ASSERT(false && "invalid LLVM type for binary operation");
452  return BinaryFunction();
453 }
454 
455 /// @brief Returns true if the llvm Type 'from' can be safely cast to the llvm
456 /// Type 'to'.
457 inline bool isValidCast(llvm::Type* from, llvm::Type* to)
458 {
459  OPENVDB_ASSERT(from && "llvm Type 'from' is null in isValidCast");
460  OPENVDB_ASSERT(to && "llvm Type 'to' is null in isValidCast");
461 
462  if ((from->isIntegerTy() || from->isFloatingPointTy()) &&
463  (to->isIntegerTy() || to->isFloatingPointTy())) {
464  return true;
465  }
466  if (from->isArrayTy() && to->isArrayTy()) {
467  llvm::ArrayType* af = llvm::cast<llvm::ArrayType>(from);
468  llvm::ArrayType* at = llvm::cast<llvm::ArrayType>(to);
469  if (af->getArrayNumElements() == at->getArrayNumElements()) {
470  return isValidCast(af->getArrayElementType(),
471  at->getArrayElementType());
472  }
473  }
474  return false;
475 }
476 
477 /// @brief Casts a scalar llvm Value to a target scalar llvm Type. Returns
478 /// the cast scalar value of type targetType.
479 /// @warning This assumes any integer types are signed.
480 /// @param value A llvm scalar value to convert
481 /// @param targetType The target llvm scalar type to convert to
482 /// @param builder The current llvm IRBuilder
483 inline llvm::Value*
484 arithmeticConversion(llvm::Value* value,
485  llvm::Type* targetType,
486  llvm::IRBuilder<>& builder)
487 {
488  OPENVDB_ASSERT(value && (value->getType()->isIntegerTy() || value->getType()->isFloatingPointTy()) &&
489  "First Value in arithmeticConversion is not a scalar type");
490  OPENVDB_ASSERT(targetType && (targetType->isIntegerTy() || targetType->isFloatingPointTy()) &&
491  "Target Type in arithmeticConversion is not a scalar type");
492 
493  const llvm::Type* const valueType = value->getType();
494  if (valueType == targetType) return value;
495 
496  CastFunction llvmCastFunction = llvmArithmeticConversion(valueType, targetType);
497  return llvmCastFunction(builder, value, targetType);
498 }
499 
500 /// @brief Converts a vector of loaded llvm scalar values of the same type to a
501 /// target scalar type. Each value is converted individually and the loaded
502 /// result stored in the same location within values.
503 /// @warning This assumes any integer types are signed.
504 /// @param values A vector of llvm scalar values to convert
505 /// @param targetElementType The target llvm scalar type to convert each value
506 /// of the input vector
507 /// @param builder The current llvm IRBuilder
508 inline void
509 arithmeticConversion(std::vector<llvm::Value*>& values,
510  llvm::Type* targetElementType,
511  llvm::IRBuilder<>& builder)
512 {
513  OPENVDB_ASSERT(targetElementType && (targetElementType->isIntegerTy() ||
514  targetElementType->isFloatingPointTy()) &&
515  "Target element type is not a scalar type");
516 
517  llvm::Type* sourceElementType = values.front()->getType();
518  OPENVDB_ASSERT(sourceElementType && (sourceElementType->isIntegerTy() ||
519  sourceElementType->isFloatingPointTy()) &&
520  "Source element type is not a scalar type");
521 
522  if (sourceElementType == targetElementType) return;
523 
524  CastFunction llvmCastFunction = llvmArithmeticConversion(sourceElementType, targetElementType);
525 
526  for (llvm::Value*& value : values) {
527  value = llvmCastFunction(builder, value, targetElementType);
528  }
529 }
530 
531 /// @brief Converts a vector of loaded llvm scalar values to the highest precision
532 /// type stored amongst them. Any values which are not scalar types are ignored
533 /// @warning This assumes any integer types are signed.
534 /// @param values A vector of llvm scalar values to convert
535 /// @param builder The current llvm IRBuilder
536 inline void
537 arithmeticConversion(std::vector<llvm::Value*>& values,
538  llvm::IRBuilder<>& builder)
539 {
540  llvm::Type* typeCast = LLVMType<bool>::get(builder.getContext());
541  for (llvm::Value*& value : values) {
542  llvm::Type* type = value->getType();
543  if (type->isIntegerTy() || type->isFloatingPointTy()) {
544  typeCast = typePrecedence(typeCast, type);
545  }
546  }
547 
548  arithmeticConversion(values, typeCast, builder);
549 }
550 
551 /// @brief Chooses the highest order llvm Type as defined by typePrecedence
552 /// from either of the two incoming values and casts the other value to
553 /// the choosen type if it is not already. The types of valueA and valueB
554 /// are guaranteed to match. Both values must be scalar LLVM types
555 /// @warning This assumes any integer types are signed.
556 /// @param valueA The first llvm value
557 /// @param valueB The second llvm value
558 /// @param builder The current llvm IRBuilder
559 inline void
560 arithmeticConversion(llvm::Value*& valueA,
561  llvm::Value*& valueB,
562  llvm::IRBuilder<>& builder)
563 {
564  llvm::Type* type = typePrecedence(valueA->getType(), valueB->getType());
565  valueA = arithmeticConversion(valueA, type, builder);
566  valueB = arithmeticConversion(valueB, type, builder);
567 }
568 
569 /// @brief Performs a C style boolean comparison from a given scalar LLVM value
570 ///
571 /// @param value The scalar llvm value to convert to a boolean
572 /// @param builder The current llvm IRBuilder
573 ///
574 inline llvm::Value*
575 boolComparison(llvm::Value* value,
576  llvm::IRBuilder<>& builder)
577 {
578  llvm::Type* type = value->getType();
579 
580  if (type->isFloatingPointTy()) return builder.CreateFCmpONE(value, llvm::ConstantFP::get(type, 0.0));
581  else if (type->isIntegerTy(1)) return builder.CreateICmpNE(value, llvm::ConstantInt::get(type, 0));
582  else if (type->isIntegerTy()) return builder.CreateICmpNE(value, llvm::ConstantInt::getSigned(type, 0));
583  OPENVDB_ASSERT(false && "Invalid type for bool conversion");
584  return nullptr;
585 }
586 
587 /// @ brief Performs a binary operation on two loaded llvm scalar values of the same type.
588 /// The type of operation performed is defined by the token (see the list of supported
589 /// tokens in ast/Tokens.h. Returns a loaded llvm scalar result
590 ///
591 /// @param lhs The left hand side value of the binary operation
592 /// @param rhs The right hand side value of the binary operation
593 /// @param token The token representing the binary operation to perform
594 /// @param builder The current llvm IRBuilder
595 inline llvm::Value*
596 binaryOperator(llvm::Value* lhs, llvm::Value* rhs,
597  const ast::tokens::OperatorToken& token,
598  llvm::IRBuilder<>& builder)
599 {
600  llvm::Type* lhsType = lhs->getType();
601  OPENVDB_ASSERT(lhsType == rhs->getType() ||
602  (token == ast::tokens::SHIFTLEFT ||
603  token == ast::tokens::SHIFTRIGHT));
604 
606 
607  if (opType == ast::tokens::LOGICAL) {
608  lhs = boolComparison(lhs, builder);
609  rhs = boolComparison(rhs, builder);
610  lhsType = lhs->getType(); // now bool type
611  }
612 
613  const BinaryFunction llvmBinaryFunction = llvmBinaryConversion(lhsType, token);
614  return llvmBinaryFunction(builder, lhs, rhs);
615 }
616 
617 } // namespace codegen
618 } // namespace ax
619 } // namespace OPENVDB_VERSION_NAME
620 } // namespace openvdb
621 
622 #endif // OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
623 
BinaryFunction llvmBinaryConversion(const llvm::Type *const type, const ast::tokens::OperatorToken &token, const std::string &twine="")
Returns a BinaryFunction representing the corresponding instruction to perform on two scalar values...
Definition: Utils.h:397
Definition: axparser.h:140
#define BIND_ARITHMETIC_CAST_OP(Function, Twine)
#define BIND_ARITHMETIC_CAST_OP_ZEXT(Function, Twine)
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Value *)> BinaryFunction
Definition: Utils.h:41
#define BIND_BINARY_OP(Function)
Definition: axparser.h:141
OperatorType operatorType(const OperatorToken token)
Definition: Tokens.h:210
Definition: axparser.h:147
Definition: axparser.h:144
OperatorType
Definition: Tokens.h:200
llvm::Value * boolComparison(llvm::Value *value, llvm::IRBuilder<> &builder)
Performs a C style boolean comparison from a given scalar LLVM value.
Definition: Utils.h:575
Definition: axparser.h:136
void valuesToTypes(const std::vector< llvm::Value * > &values, std::vector< llvm::Type * > &types)
Populate a vector of llvm Types from a vector of llvm values.
Definition: Utils.h:65
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Type *)> CastFunction
Definition: Utils.h:38
Definition: axparser.h:145
LLVM type mapping from pod types.
Definition: Types.h:67
llvm::Value * binaryOperator(llvm::Value *lhs, llvm::Value *rhs, const ast::tokens::OperatorToken &token, llvm::IRBuilder<> &builder)
Definition: Utils.h:596
Consolidated llvm types for most supported types.
Definition: axparser.h:143
llvm::Argument * extractArgument(llvm::Function *F, const std::string &name)
Definition: Utils.h:157
Definition: axparser.h:151
Definition: axparser.h:137
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
Definition: axparser.h:146
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
bool isValidCast(llvm::Type *from, llvm::Type *to)
Returns true if the llvm Type &#39;from&#39; can be safely cast to the llvm Type &#39;to&#39;.
Definition: Utils.h:457
#define BIND_ARITHMETIC_CAST_OP_TRUNC(Function, Twine)
Definition: Exceptions.h:13
#define BIND_ARITHMETIC_CAST_OP_UIFP(Function, Twine)
Definition: axparser.h:135
Definition: axparser.h:150
llvm::Value * llvmPointerFromAddress(const ValueT *const &ptr, llvm::IRBuilder<> &builder)
Return an llvm value representing a pointer to the provided ptr builtin ValueT.
Definition: Utils.h:98
Definition: axparser.h:142
Definition: axparser.h:149
#define BIND_ARITHMETIC_CAST_OP_FP(Function, Twine)
Definition: axparser.h:148
Definition: axparser.h:139
bool AssertOpaquePtrs([[maybe_unused]]llvm::Value *opaque, [[maybe_unused]]llvm::Type *type)
Definition: Utils.h:43
void llvmTypeToString(const llvm::Type *const type, std::string &str)
Prints an llvm type to a std string.
Definition: Utils.h:80
Definition: axparser.h:152
Definition: axparser.h:138
void arithmeticConversion(llvm::Value *&valueA, llvm::Value *&valueB, llvm::IRBuilder<> &builder)
Chooses the highest order llvm Type as defined by typePrecedence from either of the two incoming valu...
Definition: Utils.h:560
CastFunction llvmArithmeticConversion(const llvm::Type *const sourceType, const llvm::Type *const targetType, const std::string &twine="")
Returns a CastFunction which represents the corresponding instruction to convert a source llvm Type t...
Definition: Utils.h:220
llvm::Type * typePrecedence(llvm::Type *const typeA, llvm::Type *const typeB)
Returns the highest order type from two LLVM Scalar types.
Definition: Utils.h:173
OperatorToken
Definition: Tokens.h:150
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218