OpenVDB  9.0.1
Utils.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-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 
22 #include <llvm/IR/IRBuilder.h>
23 #include <llvm/IR/LLVMContext.h>
24 
25 // Note: As of LLVM 5.0, the llvm::Type::dump() method isn't being
26 // picked up correctly by the linker. dump() is internally implemented
27 // using Type::print(llvm::errs()) which is being used in place. See:
28 //
29 // https://stackoverflow.com/questions/43723127/llvm-5-0-makefile-undefined-reference-fail
30 //
31 #include <llvm/Support/raw_ostream.h> // llvm::errs()
32 
33 namespace openvdb {
35 namespace OPENVDB_VERSION_NAME {
36 
37 namespace ax {
38 namespace codegen {
39 
40 /// @note Function definitions for some types returned from automatic token to
41 /// llvm IR operations. See llvmArithmeticConversion and llvmBianryConversion
42 
43 using CastFunction = std::function<llvm::Value*
44  (llvm::IRBuilder<>&, llvm::Value*, llvm::Type*)>;
45 
46 using BinaryFunction = std::function<llvm::Value*
47  (llvm::IRBuilder<>&, llvm::Value*, llvm::Value*)>;
48 
49 /// @brief Populate a vector of llvm Types from a vector of llvm values
50 ///
51 /// @param values A vector of llvm values to retrieve types from
52 /// @param types A vector of llvm types to populate
53 ///
54 inline void
55 valuesToTypes(const std::vector<llvm::Value*>& values,
56  std::vector<llvm::Type*>& types)
57 {
58  types.reserve(values.size());
59  for (const auto& v : values) {
60  types.emplace_back(v->getType());
61  }
62 }
63 
64 /// @brief Prints an llvm type to a std string
65 ///
66 /// @param type The llvm type to convert
67 /// @param str The string to store the type info to
68 ///
69 inline void
70 llvmTypeToString(const llvm::Type* const type, std::string& str)
71 {
72  llvm::raw_string_ostream os(str);
73  type->print(os);
74  os.flush();
75 }
76 
77 /// @brief Return the base llvm value which is being pointed to through
78 /// any number of layered pointers.
79 /// @note This function does not check for cyclical pointer dependencies
80 ///
81 /// @param type A llvm pointer type to traverse
82 ///
83 inline llvm::Type*
84 getBaseContainedType(llvm::Type* const type)
85 {
86  llvm::Type* elementType = type;
87  while (elementType->isPointerTy()) {
88  elementType = elementType->getContainedType(0);
89  }
90  return elementType;
91 }
92 
93 /// @brief Return an llvm value representing a pointer to the provided ptr builtin
94 /// ValueT.
95 /// @note This is probably not a suitable solution for anything other than POD
96 /// types and should be used with caution.
97 ///
98 /// @param ptr A pointer to a type of ValueT whose address will be computed and
99 /// returned
100 /// @param builder The current llvm IRBuilder
101 ///
102 template <typename ValueT>
103 inline llvm::Value*
104 llvmPointerFromAddress(const ValueT* const& ptr,
105  llvm::IRBuilder<>& builder)
106 {
107  llvm::Value* address =
108  llvm::ConstantInt::get(llvm::Type::getIntNTy(builder.getContext(), sizeof(uintptr_t)*8),
109  reinterpret_cast<uintptr_t>(ptr));
110  return builder.CreateIntToPtr(address, LLVMType<ValueT*>::get(builder.getContext()));
111 }
112 
113 /// @brief Insert a stack allocation at the beginning of the current function
114 /// of the provided type and size. The IRBuilder's insertion point must
115 /// be set to a BasicBlock with a valid Function parent.
116 /// @note If a size is provided, the size must not depend on any other
117 /// instructions. If it does, invalid LLVM IR will bb generated.
118 ///
119 /// @param B The IRBuilder
120 /// @param type The type to allocate
121 /// @param size Optional count of allocations. If nullptr, runs a single allocation
122 inline llvm::Value*
123 insertStaticAlloca(llvm::IRBuilder<>& B,
124  llvm::Type* type,
125  llvm::Value* size = nullptr)
126 {
127  llvm::Type* strtype = LLVMType<codegen::String>::get(B.getContext());
128  // Create the allocation at the start of the function block
129  llvm::Function* parent = B.GetInsertBlock()->getParent();
130  assert(parent && !parent->empty());
131  auto IP = B.saveIP();
132  llvm::BasicBlock& block = parent->front();
133  if (block.empty()) B.SetInsertPoint(&block);
134  else B.SetInsertPoint(&(block.front()));
135  llvm::Value* result = B.CreateAlloca(type, size);
136 
137  /// @note Strings need to be initialised correctly when they are
138  /// created. We alloc them at the start of the function but
139  /// strings in branches may not ever be set to anything. If
140  /// we don't init these correctly, the clearup frees will
141  /// try and free uninitialised memory
142  if (type == strtype) {
143  llvm::Value* cptr = B.CreateStructGEP(strtype, result, 0); // char**
144  llvm::Value* sso = B.CreateStructGEP(strtype, result, 1); // char[]*
145  llvm::Value* sso_load = B.CreateConstGEP2_64(sso, 0 ,0); // char*
146  llvm::Value* len = B.CreateStructGEP(strtype, result, 2);
147  B.CreateStore(sso_load, cptr); // this->ptr = this->SSO;
148  B.CreateStore(B.getInt64(0), len);
149  }
150  B.restoreIP(IP);
151  return result;
152 }
153 
154 inline llvm::Argument*
155 extractArgument(llvm::Function* F, const size_t idx)
156 {
157  if (!F) return nullptr;
158  if (idx >= F->arg_size()) return nullptr;
159  return llvm::cast<llvm::Argument>(F->arg_begin() + idx);
160 }
161 
162 inline llvm::Argument*
163 extractArgument(llvm::Function* F, const std::string& name)
164 {
165  if (!F) return nullptr;
166  for (auto iter = F->arg_begin(); iter != F->arg_end(); ++iter) {
167  llvm::Argument* arg = llvm::cast<llvm::Argument>(iter);
168  if (arg->getName() == name) return arg;
169  }
170  return nullptr;
171 }
172 
173 /// @brief Returns the highest order type from two LLVM Scalar types
174 ///
175 /// @param typeA The first scalar llvm type
176 /// @param typeB The second scalar llvm type
177 ///
178 inline llvm::Type*
179 typePrecedence(llvm::Type* const typeA,
180  llvm::Type* const typeB)
181 {
182  assert(typeA && (typeA->isIntegerTy() || typeA->isFloatingPointTy()) &&
183  "First Type in typePrecedence is not a scalar type");
184  assert(typeB && (typeB->isIntegerTy() || typeB->isFloatingPointTy()) &&
185  "Second Type in typePrecedence is not a scalar type");
186 
187  // handle implicit arithmetic conversion
188  // (http://osr507doc.sco.com/en/tools/clang_conv_implicit.html)
189 
190  if (typeA->isDoubleTy()) return typeA;
191  if (typeB->isDoubleTy()) return typeB;
192 
193  if (typeA->isFloatTy()) return typeA;
194  if (typeB->isFloatTy()) return typeB;
195 
196  if (typeA->isIntegerTy(64)) return typeA;
197  if (typeB->isIntegerTy(64)) return typeB;
198 
199  if (typeA->isIntegerTy(32)) return typeA;
200  if (typeB->isIntegerTy(32)) return typeB;
201 
202  if (typeA->isIntegerTy(16)) return typeA;
203  if (typeB->isIntegerTy(16)) return typeB;
204 
205  if (typeA->isIntegerTy(8)) return typeA;
206  if (typeB->isIntegerTy(8)) return typeB;
207 
208  if (typeA->isIntegerTy(1)) return typeA;
209  if (typeB->isIntegerTy(1)) return typeB;
210 
211  assert(false && "invalid LLVM type precedence");
212  return nullptr;
213 }
214 
215 /// @brief Returns a CastFunction which represents the corresponding instruction
216 /// to convert a source llvm Type to a target llvm Type. If the conversion
217 /// is unsupported, throws an error.
218 ///
219 /// @param sourceType The source type to cast
220 /// @param targetType The target type to cast to
221 /// @param twine An optional string description of the cast function. This can
222 /// be used for for more verbose llvm information on IR compilation
223 /// failure
224 inline CastFunction
225 llvmArithmeticConversion(const llvm::Type* const sourceType,
226  const llvm::Type* const targetType,
227  const std::string& twine = "")
228 {
229 
230 #define BIND_ARITHMETIC_CAST_OP(Function, Twine) \
231  std::bind(&Function, \
232  std::placeholders::_1, \
233  std::placeholders::_2, \
234  std::placeholders::_3, \
235  Twine)
236 
237  if (targetType->isDoubleTy()) {
238  if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPExt, twine);
239  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
240  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
241  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
242  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
243  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateUIToFP, twine);
244  }
245  else if (targetType->isFloatTy()) {
246  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPTrunc, twine);
247  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
248  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
249  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
250  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
251  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateUIToFP, twine);
252  }
253  else if (targetType->isIntegerTy(64)) {
254  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
255  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
256  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
257  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
258  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
259  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
260  }
261  else if (targetType->isIntegerTy(32)) {
262  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
263  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
264  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
265  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
266  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
267  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
268  }
269  else if (targetType->isIntegerTy(16)) {
270  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
271  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
272  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
273  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
274  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
275  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
276  }
277  else if (targetType->isIntegerTy(8)) {
278  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
279  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
280  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
281  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
282  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
283  else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
284  }
285  else if (targetType->isIntegerTy(1)) {
286  if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
287  else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
288  else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
289  else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
290  else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
291  else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
292  }
293 
294 #undef BIND_ARITHMETIC_CAST_OP
295  assert(false && "invalid LLVM type conversion");
296  return CastFunction();
297 }
298 
299 /// @brief Returns a BinaryFunction representing the corresponding instruction to
300 /// perform on two scalar values, relative to a provided operator token. Note that
301 /// not all operations are supported on floating point types! If the token is not
302 /// supported, or the llvm type is not a scalar type, throws an error.
303 /// @note Various default arguments are bound to provide a simple function call
304 /// signature. For floating point operations, this includes a null pointer to
305 /// the optional metadata node. For integer operations, this includes disabling
306 /// all overflow/rounding optimisations
307 ///
308 /// @param type The type defining the precision of the binary operation
309 /// @param token The token used to create the relative binary operation
310 /// @param twine An optional string description of the binary function. This can
311 /// be used for for more verbose llvm information on IR compilation
312 /// failure
313 inline BinaryFunction
314 llvmBinaryConversion(const llvm::Type* const type,
315  const ast::tokens::OperatorToken& token,
316  const std::string& twine = "")
317 {
318 
319 #define BIND_BINARY_OP(Function) \
320  [twine](llvm::IRBuilder<>& B, llvm::Value* L, llvm::Value* R) \
321  -> llvm::Value* { return B.Function(L, R, twine); }
322 
323  // NOTE: Binary % and / ops always take sign into account (CreateSDiv vs CreateUDiv, CreateSRem vs CreateURem).
324  // See http://stackoverflow.com/questions/5346160/llvm-irbuildercreateudiv-createsdiv-createexactudiv
325  // a%b in AX is implemented as a floored modulo op and is handled explicitly in binaryExpression
326 
327  if (type->isFloatingPointTy()) {
328  assert(!(ast::tokens::operatorType(token) == ast::tokens::LOGICAL ||
330  && "unable to perform logical or bitwise operation on floating point values");
331 
332  if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateFAdd);
333  else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateFSub);
334  else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateFMul);
335  else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateFDiv);
336  else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateFRem); // Note this is NOT a%b in AX.
337  else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateFCmpOEQ);
338  else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateFCmpONE);
339  else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateFCmpOGT);
340  else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateFCmpOLT);
341  else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOGE);
342  else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOLE);
343  assert(false && "unrecognised binary operator");
344  }
345  else if (type->isIntegerTy()) {
346  if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateAdd); // No Unsigned/Signed Wrap
347  else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateSub); // No Unsigned/Signed Wrap
348  else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateMul); // No Unsigned/Signed Wrap
349  else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateSDiv); // IsExact = false - when true, poison value if the reuslt is rounded
350  else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateSRem); // Note this is NOT a%b in AX.
351  else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateICmpEQ);
352  else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateICmpNE);
353  else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateICmpSGT);
354  else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateICmpSLT);
355  else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateICmpSGE);
356  else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateICmpSLE);
357  else if (token == ast::tokens::AND) return BIND_BINARY_OP(CreateAnd);
358  else if (token == ast::tokens::OR) return BIND_BINARY_OP(CreateOr);
359  else if (token == ast::tokens::SHIFTLEFT) return BIND_BINARY_OP(CreateShl); // No Unsigned/Signed Wrap
360  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.
361  else if (token == ast::tokens::BITAND) return BIND_BINARY_OP(CreateAnd);
362  else if (token == ast::tokens::BITOR) return BIND_BINARY_OP(CreateOr);
363  else if (token == ast::tokens::BITXOR) return BIND_BINARY_OP(CreateXor);
364  assert(false && "unrecognised binary operator");
365  }
366 
367 #undef BIND_BINARY_OP
368  assert(false && "invalid LLVM type for binary operation");
369  return BinaryFunction();
370 }
371 
372 /// @brief Returns true if the llvm Type 'from' can be safely cast to the llvm
373 /// Type 'to'.
374 inline bool isValidCast(llvm::Type* from, llvm::Type* to)
375 {
376  assert(from && "llvm Type 'from' is null in isValidCast");
377  assert(to && "llvm Type 'to' is null in isValidCast");
378 
379  if ((from->isIntegerTy() || from->isFloatingPointTy()) &&
380  (to->isIntegerTy() || to->isFloatingPointTy())) {
381  return true;
382  }
383  if (from->isArrayTy() && to->isArrayTy()) {
384  llvm::ArrayType* af = llvm::cast<llvm::ArrayType>(from);
385  llvm::ArrayType* at = llvm::cast<llvm::ArrayType>(to);
386  if (af->getArrayNumElements() == at->getArrayNumElements()) {
387  return isValidCast(af->getArrayElementType(),
388  at->getArrayElementType());
389  }
390  }
391  return false;
392 }
393 
394 /// @brief Casts a scalar llvm Value to a target scalar llvm Type. Returns
395 /// the cast scalar value of type targetType.
396 ///
397 /// @param value A llvm scalar value to convert
398 /// @param targetType The target llvm scalar type to convert to
399 /// @param builder The current llvm IRBuilder
400 ///
401 inline llvm::Value*
403  llvm::Type* targetType,
404  llvm::IRBuilder<>& builder)
405 {
406  assert(value && (value->getType()->isIntegerTy() || value->getType()->isFloatingPointTy()) &&
407  "First Value in arithmeticConversion is not a scalar type");
408  assert(targetType && (targetType->isIntegerTy() || targetType->isFloatingPointTy()) &&
409  "Target Type in arithmeticConversion is not a scalar type");
410 
411  const llvm::Type* const valueType = value->getType();
412  if (valueType == targetType) return value;
413 
414  CastFunction llvmCastFunction = llvmArithmeticConversion(valueType, targetType);
415  return llvmCastFunction(builder, value, targetType);
416 }
417 
418 /// @brief Casts an array to another array of equal size but of a different element
419 /// type. Both source and target array element types must be scalar types.
420 /// The source array llvm Value should be a pointer to the array to cast.
421 ///
422 /// @param ptrToArray A llvm value which is a pointer to a llvm array
423 /// @param targetElementType The target llvm scalar type to convert each element
424 /// of the input array
425 /// @param builder The current llvm IRBuilder
426 ///
427 inline llvm::Value*
428 arrayCast(llvm::Value* ptrToArray,
429  llvm::Type* targetElementType,
430  llvm::IRBuilder<>& builder)
431 {
432  assert(targetElementType && (targetElementType->isIntegerTy() ||
433  targetElementType->isFloatingPointTy()) &&
434  "Target element type is not a scalar type");
435  assert(ptrToArray && ptrToArray->getType()->isPointerTy() &&
436  "Input to arrayCast is not a pointer type.");
437 
438  llvm::Type* arrayType = ptrToArray->getType()->getContainedType(0);
439  assert(arrayType && llvm::isa<llvm::ArrayType>(arrayType));
440 
441  // getArrayElementType() calls getContainedType(0)
442  llvm::Type* sourceElementType = arrayType->getArrayElementType();
443  assert(sourceElementType && (sourceElementType->isIntegerTy() ||
444  sourceElementType->isFloatingPointTy()) &&
445  "Source element type is not a scalar type");
446 
447  if (sourceElementType == targetElementType) return ptrToArray;
448 
449  CastFunction llvmCastFunction = llvmArithmeticConversion(sourceElementType, targetElementType);
450 
451  const size_t elementSize = arrayType->getArrayNumElements();
452  llvm::Value* targetArray =
453  insertStaticAlloca(builder,
454  llvm::ArrayType::get(targetElementType, elementSize));
455 
456  for (size_t i = 0; i < elementSize; ++i) {
457  llvm::Value* target = builder.CreateConstGEP2_64(targetArray, 0, i);
458  llvm::Value* source = builder.CreateConstGEP2_64(ptrToArray, 0, i);
459  source = builder.CreateLoad(source);
460  source = llvmCastFunction(builder, source, targetElementType);
461  builder.CreateStore(source, target);
462  }
463 
464  return targetArray;
465 }
466 
467 /// @brief Converts a vector of loaded llvm scalar values of the same type to a
468 /// target scalar type. Each value is converted individually and the loaded result
469 /// stored in the same location within values.
470 ///
471 /// @param values A vector of llvm scalar values to convert
472 /// @param targetElementType The target llvm scalar type to convert each value
473 /// of the input vector
474 /// @param builder The current llvm IRBuilder
475 ///
476 inline void
477 arithmeticConversion(std::vector<llvm::Value*>& values,
478  llvm::Type* targetElementType,
479  llvm::IRBuilder<>& builder)
480 {
481  assert(targetElementType && (targetElementType->isIntegerTy() ||
482  targetElementType->isFloatingPointTy()) &&
483  "Target element type is not a scalar type");
484 
485  llvm::Type* sourceElementType = values.front()->getType();
486  assert(sourceElementType && (sourceElementType->isIntegerTy() ||
487  sourceElementType->isFloatingPointTy()) &&
488  "Source element type is not a scalar type");
489 
490  if (sourceElementType == targetElementType) return;
491 
492  CastFunction llvmCastFunction = llvmArithmeticConversion(sourceElementType, targetElementType);
493 
494  for (llvm::Value*& value : values) {
495  value = llvmCastFunction(builder, value, targetElementType);
496  }
497 }
498 
499 /// @brief Converts a vector of loaded llvm scalar values to the highest precision
500 /// type stored amongst them. Any values which are not scalar types are ignored
501 ///
502 /// @param values A vector of llvm scalar values to convert
503 /// @param builder The current llvm IRBuilder
504 ///
505 inline void
506 arithmeticConversion(std::vector<llvm::Value*>& values,
507  llvm::IRBuilder<>& builder)
508 {
509  llvm::Type* typeCast = LLVMType<bool>::get(builder.getContext());
510  for (llvm::Value*& value : values) {
511  llvm::Type* type = value->getType();
512  if (type->isIntegerTy() || type->isFloatingPointTy()) {
513  typeCast = typePrecedence(typeCast, type);
514  }
515  }
516 
517  arithmeticConversion(values, typeCast, builder);
518 }
519 
520 /// @brief Chooses the highest order llvm Type as defined by typePrecedence
521 /// from either of the two incoming values and casts the other value to
522 /// the choosen type if it is not already. The types of valueA and valueB
523 /// are guaranteed to match. Both values must be scalar LLVM types
524 ///
525 /// @param valueA The first llvm value
526 /// @param valueB The second llvm value
527 /// @param builder The current llvm IRBuilder
528 ///
529 inline void
530 arithmeticConversion(llvm::Value*& valueA,
531  llvm::Value*& valueB,
532  llvm::IRBuilder<>& builder)
533 {
534  llvm::Type* type = typePrecedence(valueA->getType(), valueB->getType());
535  valueA = arithmeticConversion(valueA, type, builder);
536  valueB = arithmeticConversion(valueB, type, builder);
537 }
538 
539 /// @brief Performs a C style boolean comparison from a given scalar LLVM value
540 ///
541 /// @param value The scalar llvm value to convert to a boolean
542 /// @param builder The current llvm IRBuilder
543 ///
544 inline llvm::Value*
545 boolComparison(llvm::Value* value,
546  llvm::IRBuilder<>& builder)
547 {
548  llvm::Type* type = value->getType();
549 
550  if (type->isFloatingPointTy()) return builder.CreateFCmpONE(value, llvm::ConstantFP::get(type, 0.0));
551  else if (type->isIntegerTy(1)) return builder.CreateICmpNE(value, llvm::ConstantInt::get(type, 0));
552  else if (type->isIntegerTy()) return builder.CreateICmpNE(value, llvm::ConstantInt::getSigned(type, 0));
553  assert(false && "Invalid type for bool conversion");
554  return nullptr;
555 }
556 
557 /// @ brief Performs a binary operation on two loaded llvm scalar values of the same type.
558 /// The type of operation performed is defined by the token (see the list of supported
559 /// tokens in ast/Tokens.h. Returns a loaded llvm scalar result
560 ///
561 /// @param lhs The left hand side value of the binary operation
562 /// @param rhs The right hand side value of the binary operation
563 /// @param token The token representing the binary operation to perform
564 /// @param builder The current llvm IRBuilder
565 inline llvm::Value*
566 binaryOperator(llvm::Value* lhs, llvm::Value* rhs,
567  const ast::tokens::OperatorToken& token,
568  llvm::IRBuilder<>& builder)
569 {
570  llvm::Type* lhsType = lhs->getType();
571  assert(lhsType == rhs->getType() ||
572  (token == ast::tokens::SHIFTLEFT ||
573  token == ast::tokens::SHIFTRIGHT));
574 
576 
577  if (opType == ast::tokens::LOGICAL) {
578  lhs = boolComparison(lhs, builder);
579  rhs = boolComparison(rhs, builder);
580  lhsType = lhs->getType(); // now bool type
581  }
582 
583  const BinaryFunction llvmBinaryFunction = llvmBinaryConversion(lhsType, token);
584  return llvmBinaryFunction(builder, lhs, rhs);
585 }
586 
587 /// @brief Unpack a particular element of an array and return a pointer to that element
588 /// The provided llvm Value is expected to be a pointer to an array
589 ///
590 /// @param ptrToArray A llvm value which is a pointer to a llvm array
591 /// @param index The index at which to access the array
592 /// @param builder The current llvm IRBuilder
593 ///
594 inline llvm::Value*
595 arrayIndexUnpack(llvm::Value* ptrToArray,
596  const int16_t index,
597  llvm::IRBuilder<>& builder)
598 {
599  return builder.CreateConstGEP2_64(ptrToArray, 0, index);
600 }
601 
602 /// @brief Unpack an array type into llvm Values which represent all its elements
603 /// The provided llvm Value is expected to be a pointer to an array
604 /// If loadElements is true, values will store loaded llvm values instead
605 /// of pointers to the array elements
606 ///
607 /// @param ptrToArray A llvm value which is a pointer to a llvm array
608 /// @param values A vector of llvm values where to store the array elements
609 /// @param builder The current llvm IRBuilder
610 /// @param loadElements Whether or not to load each array element into a register
611 ///
612 inline void
613 arrayUnpack(llvm::Value* ptrToArray,
614  std::vector<llvm::Value*>& values,
615  llvm::IRBuilder<>& builder,
616  const bool loadElements = false)
617 {
618  const size_t elements =
619  ptrToArray->getType()->getContainedType(0)->getArrayNumElements();
620 
621  values.reserve(elements);
622  for (size_t i = 0; i < elements; ++i) {
623  llvm::Value* value = builder.CreateConstGEP2_64(ptrToArray, 0, i);
624  if (loadElements) value = builder.CreateLoad(value);
625  values.push_back(value);
626  }
627 }
628 
629 /// @brief Unpack the first three elements of an array.
630 /// The provided llvm Value is expected to be a pointer to an array
631 /// @note The elements are note loaded
632 ///
633 /// @param ptrToArray A llvm value which is a pointer to a llvm array
634 /// @param value1 The first array value
635 /// @param value2 The second array value
636 /// @param value3 The third array value
637 /// @param builder The current llvm IRBuilder
638 ///
639 inline void
640 array3Unpack(llvm::Value* ptrToArray,
641  llvm::Value*& value1,
642  llvm::Value*& value2,
643  llvm::Value*& value3,
644  llvm::IRBuilder<>& builder)
645 {
646  assert(ptrToArray && ptrToArray->getType()->isPointerTy() &&
647  "Input to array3Unpack is not a pointer type.");
648 
649  value1 = builder.CreateConstGEP2_64(ptrToArray, 0, 0);
650  value2 = builder.CreateConstGEP2_64(ptrToArray, 0, 1);
651  value3 = builder.CreateConstGEP2_64(ptrToArray, 0, 2);
652 }
653 
654 /// @brief Pack three values into a new array and return a pointer to the
655 /// newly allocated array. If the values are of a mismatching type,
656 /// the highets order type is uses, as defined by typePrecedence. All
657 /// llvm values are expected to a be a loaded scalar type
658 ///
659 /// @param value1 The first array value
660 /// @param value2 The second array value
661 /// @param value3 The third array value
662 /// @param builder The current llvm IRBuilder
663 ///
664 inline llvm::Value*
665 array3Pack(llvm::Value* value1,
666  llvm::Value* value2,
667  llvm::Value* value3,
668  llvm::IRBuilder<>& builder)
669 {
670  llvm::Type* type = typePrecedence(value1->getType(), value2->getType());
671  type = typePrecedence(type, value3->getType());
672 
673  value1 = arithmeticConversion(value1, type, builder);
674  value2 = arithmeticConversion(value2, type, builder);
675  value3 = arithmeticConversion(value3, type, builder);
676 
677  llvm::Type* vectorType = llvm::ArrayType::get(type, 3);
678  llvm::Value* vector = insertStaticAlloca(builder, vectorType);
679 
680  llvm::Value* e1 = builder.CreateConstGEP2_64(vector, 0, 0);
681  llvm::Value* e2 = builder.CreateConstGEP2_64(vector, 0, 1);
682  llvm::Value* e3 = builder.CreateConstGEP2_64(vector, 0, 2);
683 
684  builder.CreateStore(value1, e1);
685  builder.CreateStore(value2, e2);
686  builder.CreateStore(value3, e3);
687 
688  return vector;
689 }
690 
691 /// @brief Pack a loaded llvm scalar value into a new array of a specified
692 /// size and return a pointer to the newly allocated array. Each element
693 /// of the new array will have the value of the given scalar
694 ///
695 /// @param value The uniform scalar llvm value to pack into the array
696 /// @param builder The current llvm IRBuilder
697 /// @param size The size of the newly allocated array
698 ///
699 inline llvm::Value*
700 arrayPack(llvm::Value* value,
701  llvm::IRBuilder<>& builder,
702  const size_t size = 3)
703 {
704  assert(value && (value->getType()->isIntegerTy() ||
705  value->getType()->isFloatingPointTy()) &&
706  "value type is not a scalar type");
707 
708  llvm::Type* type = value->getType();
709  llvm::Value* array =
710  insertStaticAlloca(builder,
711  llvm::ArrayType::get(type, size));
712 
713  for (size_t i = 0; i < size; ++i) {
714  llvm::Value* element = builder.CreateConstGEP2_64(array, 0, i);
715  builder.CreateStore(value, element);
716  }
717 
718  return array;
719 }
720 
721 /// @brief Pack a vector of loaded llvm scalar values into a new array of
722 /// equal size and return a pointer to the newly allocated array.
723 ///
724 /// @param values A vector of loaded llvm scalar values to pack
725 /// @param builder The current llvm IRBuilder
726 ///
727 inline llvm::Value*
728 arrayPack(const std::vector<llvm::Value*>& values,
729  llvm::IRBuilder<>& builder)
730 {
731  llvm::Type* type = values.front()->getType();
732  llvm::Value* array = insertStaticAlloca(builder,
733  llvm::ArrayType::get(type, values.size()));
734 
735  size_t idx = 0;
736  for (llvm::Value* const& value : values) {
737  llvm::Value* element = builder.CreateConstGEP2_64(array, 0, idx++);
738  builder.CreateStore(value, element);
739  }
740 
741  return array;
742 }
743 
744 /// @brief Pack a vector of loaded llvm scalar values into a new array of
745 /// equal size and return a pointer to the newly allocated array.
746 /// arrayPackCast first checks all the contained types in values
747 /// and casts all types to the highest order type present. All llvm
748 /// values in values are expected to be loaded scalar types
749 ///
750 /// @param values A vector of loaded llvm scalar values to pack
751 /// @param builder The current llvm IRBuilder
752 ///
753 inline llvm::Value*
754 arrayPackCast(std::vector<llvm::Value*>& values,
755  llvm::IRBuilder<>& builder)
756 {
757  // get the highest order type present
758 
759  llvm::Type* type = LLVMType<bool>::get(builder.getContext());
760  for (llvm::Value* const& value : values) {
761  type = typePrecedence(type, value->getType());
762  }
763 
764  // convert all to this type
765 
766  for (llvm::Value*& value : values) {
767  value = arithmeticConversion(value, type, builder);
768  }
769 
770  return arrayPack(values, builder);
771 }
772 
773 inline llvm::Value*
774 scalarToMatrix(llvm::Value* scalar,
775  llvm::IRBuilder<>& builder,
776  const size_t dim = 3)
777 {
778  assert(scalar && (scalar->getType()->isIntegerTy() ||
779  scalar->getType()->isFloatingPointTy()) &&
780  "value type is not a scalar type");
781 
782  llvm::Type* type = scalar->getType();
783  llvm::Value* array =
784  insertStaticAlloca(builder,
785  llvm::ArrayType::get(type, dim*dim));
786 
787  llvm::Value* zero = llvmConstant(0, type);
788  for (size_t i = 0; i < dim*dim; ++i) {
789  llvm::Value* m = ((i % (dim+1) == 0) ? scalar : zero);
790  llvm::Value* element = builder.CreateConstGEP2_64(array, 0, i);
791  builder.CreateStore(m, element);
792  }
793 
794  return array;
795 }
796 
797 } // namespace codegen
798 } // namespace ax
799 } // namespace OPENVDB_VERSION_NAME
800 } // namespace openvdb
801 
802 #endif // OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
803 
Definition: axparser.h:131
llvm::Type * getBaseContainedType(llvm::Type *const type)
Return the base llvm value which is being pointed to through any number of layered pointers...
Definition: Utils.h:84
#define BIND_ARITHMETIC_CAST_OP(Function, Twine)
#define BIND_BINARY_OP(Function)
Definition: axparser.h:132
Definition: axparser.h:138
Definition: axparser.h:135
Definition: axparser.h:127
Definition: axparser.h:136
Consolidated llvm types for most supported types.
void array3Unpack(llvm::Value *ptrToArray, llvm::Value *&value1, llvm::Value *&value2, llvm::Value *&value3, llvm::IRBuilder<> &builder)
Unpack the first three elements of an array. The provided llvm Value is expected to be a pointer to a...
Definition: Utils.h:640
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:104
Definition: axparser.h:134
llvm::Value * scalarToMatrix(llvm::Value *scalar, llvm::IRBuilder<> &builder, const size_t dim=3)
Definition: Utils.h:774
llvm::Value * arrayIndexUnpack(llvm::Value *ptrToArray, const int16_t index, llvm::IRBuilder<> &builder)
Unpack a particular element of an array and return a pointer to that element The provided llvm Value ...
Definition: Utils.h:595
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Type *)> CastFunction
Definition: Utils.h:44
OperatorToken
Definition: Tokens.h:150
llvm::Value * arrayPack(const std::vector< llvm::Value * > &values, llvm::IRBuilder<> &builder)
Pack a vector of loaded llvm scalar values into a new array of equal size and return a pointer to the...
Definition: Utils.h:728
llvm::Value * boolComparison(llvm::Value *value, llvm::IRBuilder<> &builder)
Performs a C style boolean comparison from a given scalar LLVM value.
Definition: Utils.h:545
Definition: axparser.h:142
Definition: axparser.h:128
llvm::Type * typePrecedence(llvm::Type *const typeA, llvm::Type *const typeB)
Returns the highest order type from two LLVM Scalar types.
Definition: Utils.h:179
Definition: axparser.h:137
llvm::Value * arrayCast(llvm::Value *ptrToArray, llvm::Type *targetElementType, llvm::IRBuilder<> &builder)
Casts an array to another array of equal size but of a different element type. Both source and target...
Definition: Utils.h:428
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:314
Definition: Exceptions.h:13
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:55
llvm::Value * array3Pack(llvm::Value *value1, llvm::Value *value2, llvm::Value *value3, llvm::IRBuilder<> &builder)
Pack three values into a new array and return a pointer to the newly allocated array. If the values are of a mismatching type, the highets order type is uses, as defined by typePrecedence. All llvm values are expected to a be a loaded scalar type.
Definition: Utils.h:665
ValueT value
Definition: GridBuilder.h:1287
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:530
Definition: axparser.h:126
Definition: axparser.h:141
LLVM type mapping from pod types.
Definition: Types.h:54
Definition: axparser.h:133
Definition: axparser.h:140
Definition: axparser.h:139
llvm::Value * arrayPackCast(std::vector< llvm::Value * > &values, llvm::IRBuilder<> &builder)
Pack a vector of loaded llvm scalar values into a new array of equal size and return a pointer to the...
Definition: Utils.h:754
Definition: axparser.h:130
void arrayUnpack(llvm::Value *ptrToArray, std::vector< llvm::Value * > &values, llvm::IRBuilder<> &builder, const bool loadElements=false)
Unpack an array type into llvm Values which represent all its elements The provided llvm Value is exp...
Definition: Utils.h:613
OperatorType operatorType(const OperatorToken token)
Definition: Tokens.h:210
Definition: axparser.h:143
Definition: axparser.h:129
llvm::Argument * extractArgument(llvm::Function *F, const std::string &name)
Definition: Utils.h:163
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:225
llvm::Constant * llvmConstant(const T t, llvm::Type *type)
Returns an llvm Constant holding a scalar value.
Definition: Types.h:308
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Value *)> BinaryFunction
Definition: Utils.h:47
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:374
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
llvm::Value * binaryOperator(llvm::Value *lhs, llvm::Value *rhs, const ast::tokens::OperatorToken &token, llvm::IRBuilder<> &builder)
Definition: Utils.h:566
OperatorType
Definition: Tokens.h:200
void llvmTypeToString(const llvm::Type *const type, std::string &str)
Prints an llvm type to a std string.
Definition: Utils.h:70
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
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:123