OpenVDB 9.1.0
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
33namespace openvdb {
35namespace OPENVDB_VERSION_NAME {
36
37namespace ax {
38namespace codegen {
39
40/// @note Function definitions for some types returned from automatic token to
41/// llvm IR operations. See llvmArithmeticConversion and llvmBianryConversion
42
43using CastFunction = std::function<llvm::Value*
44 (llvm::IRBuilder<>&, llvm::Value*, llvm::Type*)>;
45
46using 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///
54inline void
55valuesToTypes(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///
69inline void
70llvmTypeToString(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///
83inline llvm::Type*
84getBaseContainedType(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///
102template <typename ValueT>
103inline llvm::Value*
104llvmPointerFromAddress(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
122inline llvm::Value*
123insertStaticAlloca(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
154inline llvm::Argument*
155extractArgument(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
162inline llvm::Argument*
163extractArgument(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///
178inline llvm::Type*
179typePrecedence(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/// @warning This assumes any integer types are signed.
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
224inline CastFunction
225llvmArithmeticConversion(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->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPExt, twine);
240 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
241 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
242 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
243 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
244 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateUIToFP, twine);
245 }
246 else if (targetType->isFloatTy()) {
247 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPTrunc, twine);
248 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPExt, twine);
249 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
250 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
251 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
252 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
253 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateUIToFP, twine);
254 }
255 else if (targetType->isHalfTy()) {
256 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPTrunc, twine);
257 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPTrunc, twine);
258 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
259 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
260 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
261 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
262 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateUIToFP, twine);
263 }
264 else if (targetType->isIntegerTy(64)) {
265 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
266 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
267 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
268 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
269 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
270 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
271 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
272 }
273 else if (targetType->isIntegerTy(32)) {
274 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
275 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
276 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
277 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
278 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
279 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
280 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
281 }
282 else if (targetType->isIntegerTy(16)) {
283 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
284 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
285 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
286 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
287 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
288 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
289 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
290 }
291 else if (targetType->isIntegerTy(8)) {
292 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
293 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
294 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
295 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
296 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
297 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
298 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateZExt, twine);
299 }
300 else if (targetType->isIntegerTy(1)) {
301 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
302 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
303 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
304 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
305 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
306 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
307 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateTrunc, twine);
308 }
309
310#undef BIND_ARITHMETIC_CAST_OP
311 assert(false && "invalid LLVM type conversion");
312 return CastFunction();
313}
314
315/// @brief Returns a BinaryFunction representing the corresponding instruction to
316/// perform on two scalar values, relative to a provided operator token. Note that
317/// not all operations are supported on floating point types! If the token is not
318/// supported, or the llvm type is not a scalar type, throws an error.
319/// @note Various default arguments are bound to provide a simple function call
320/// signature. For floating point operations, this includes a null pointer to
321/// the optional metadata node. For integer operations, this includes disabling
322/// all overflow/rounding optimisations
323///
324/// @param type The type defining the precision of the binary operation
325/// @param token The token used to create the relative binary operation
326/// @param twine An optional string description of the binary function. This can
327/// be used for for more verbose llvm information on IR compilation
328/// failure
329inline BinaryFunction
330llvmBinaryConversion(const llvm::Type* const type,
331 const ast::tokens::OperatorToken& token,
332 const std::string& twine = "")
333{
334
335#define BIND_BINARY_OP(Function) \
336 [twine](llvm::IRBuilder<>& B, llvm::Value* L, llvm::Value* R) \
337 -> llvm::Value* { return B.Function(L, R, twine); }
338
339 // NOTE: Binary % and / ops always take sign into account (CreateSDiv vs CreateUDiv, CreateSRem vs CreateURem).
340 // See http://stackoverflow.com/questions/5346160/llvm-irbuildercreateudiv-createsdiv-createexactudiv
341 // a%b in AX is implemented as a floored modulo op and is handled explicitly in binaryExpression
342
343 if (type->isFloatingPointTy()) {
346 && "unable to perform logical or bitwise operation on floating point values");
347
348 if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateFAdd);
349 else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateFSub);
350 else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateFMul);
351 else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateFDiv);
352 else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateFRem); // Note this is NOT a%b in AX.
353 else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateFCmpOEQ);
354 else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateFCmpONE);
355 else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateFCmpOGT);
356 else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateFCmpOLT);
357 else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOGE);
358 else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOLE);
359 assert(false && "unrecognised binary operator");
360 }
361 else if (type->isIntegerTy()) {
362 if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateAdd); // No Unsigned/Signed Wrap
363 else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateSub); // No Unsigned/Signed Wrap
364 else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateMul); // No Unsigned/Signed Wrap
365 else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateSDiv); // IsExact = false - when true, poison value if the reuslt is rounded
366 else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateSRem); // Note this is NOT a%b in AX.
367 else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateICmpEQ);
368 else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateICmpNE);
369 else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateICmpSGT);
370 else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateICmpSLT);
371 else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateICmpSGE);
372 else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateICmpSLE);
373 else if (token == ast::tokens::AND) return BIND_BINARY_OP(CreateAnd);
374 else if (token == ast::tokens::OR) return BIND_BINARY_OP(CreateOr);
375 else if (token == ast::tokens::SHIFTLEFT) return BIND_BINARY_OP(CreateShl); // No Unsigned/Signed Wrap
376 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.
377 else if (token == ast::tokens::BITAND) return BIND_BINARY_OP(CreateAnd);
378 else if (token == ast::tokens::BITOR) return BIND_BINARY_OP(CreateOr);
379 else if (token == ast::tokens::BITXOR) return BIND_BINARY_OP(CreateXor);
380 assert(false && "unrecognised binary operator");
381 }
382
383#undef BIND_BINARY_OP
384 assert(false && "invalid LLVM type for binary operation");
385 return BinaryFunction();
386}
387
388/// @brief Returns true if the llvm Type 'from' can be safely cast to the llvm
389/// Type 'to'.
390inline bool isValidCast(llvm::Type* from, llvm::Type* to)
391{
392 assert(from && "llvm Type 'from' is null in isValidCast");
393 assert(to && "llvm Type 'to' is null in isValidCast");
394
395 if ((from->isIntegerTy() || from->isFloatingPointTy()) &&
396 (to->isIntegerTy() || to->isFloatingPointTy())) {
397 return true;
398 }
399 if (from->isArrayTy() && to->isArrayTy()) {
400 llvm::ArrayType* af = llvm::cast<llvm::ArrayType>(from);
401 llvm::ArrayType* at = llvm::cast<llvm::ArrayType>(to);
402 if (af->getArrayNumElements() == at->getArrayNumElements()) {
403 return isValidCast(af->getArrayElementType(),
404 at->getArrayElementType());
405 }
406 }
407 return false;
408}
409
410/// @brief Casts a scalar llvm Value to a target scalar llvm Type. Returns
411/// the cast scalar value of type targetType.
412/// @warning This assumes any integer types are signed.
413/// @param value A llvm scalar value to convert
414/// @param targetType The target llvm scalar type to convert to
415/// @param builder The current llvm IRBuilder
416inline llvm::Value*
418 llvm::Type* targetType,
419 llvm::IRBuilder<>& builder)
420{
421 assert(value && (value->getType()->isIntegerTy() || value->getType()->isFloatingPointTy()) &&
422 "First Value in arithmeticConversion is not a scalar type");
423 assert(targetType && (targetType->isIntegerTy() || targetType->isFloatingPointTy()) &&
424 "Target Type in arithmeticConversion is not a scalar type");
425
426 const llvm::Type* const valueType = value->getType();
427 if (valueType == targetType) return value;
428
429 CastFunction llvmCastFunction = llvmArithmeticConversion(valueType, targetType);
430 return llvmCastFunction(builder, value, targetType);
431}
432
433/// @brief Casts an array to another array of equal size but of a different element
434/// type. Both source and target array element types must be scalar types.
435/// The source array llvm Value should be a pointer to the array to cast.
436///
437/// @param ptrToArray A llvm value which is a pointer to a llvm array
438/// @param targetElementType The target llvm scalar type to convert each element
439/// of the input array
440/// @param builder The current llvm IRBuilder
441///
442inline llvm::Value*
443arrayCast(llvm::Value* ptrToArray,
444 llvm::Type* targetElementType,
445 llvm::IRBuilder<>& builder)
446{
447 assert(targetElementType && (targetElementType->isIntegerTy() ||
448 targetElementType->isFloatingPointTy()) &&
449 "Target element type is not a scalar type");
450 assert(ptrToArray && ptrToArray->getType()->isPointerTy() &&
451 "Input to arrayCast is not a pointer type.");
452
453 llvm::Type* arrayType = ptrToArray->getType()->getContainedType(0);
454 assert(arrayType && llvm::isa<llvm::ArrayType>(arrayType));
455
456 // getArrayElementType() calls getContainedType(0)
457 llvm::Type* sourceElementType = arrayType->getArrayElementType();
458 assert(sourceElementType && (sourceElementType->isIntegerTy() ||
459 sourceElementType->isFloatingPointTy()) &&
460 "Source element type is not a scalar type");
461
462 if (sourceElementType == targetElementType) return ptrToArray;
463
464 CastFunction llvmCastFunction = llvmArithmeticConversion(sourceElementType, targetElementType);
465
466 const size_t elementSize = arrayType->getArrayNumElements();
467 llvm::Value* targetArray =
468 insertStaticAlloca(builder,
469 llvm::ArrayType::get(targetElementType, elementSize));
470
471 for (size_t i = 0; i < elementSize; ++i) {
472 llvm::Value* target = builder.CreateConstGEP2_64(targetArray, 0, i);
473 llvm::Value* source = builder.CreateConstGEP2_64(ptrToArray, 0, i);
474 source = builder.CreateLoad(source);
475 source = llvmCastFunction(builder, source, targetElementType);
476 builder.CreateStore(source, target);
477 }
478
479 return targetArray;
480}
481
482/// @brief Converts a vector of loaded llvm scalar values of the same type to a
483/// target scalar type. Each value is converted individually and the loaded
484/// result stored in the same location within values.
485/// @warning This assumes any integer types are signed.
486/// @param values A vector of llvm scalar values to convert
487/// @param targetElementType The target llvm scalar type to convert each value
488/// of the input vector
489/// @param builder The current llvm IRBuilder
490inline void
491arithmeticConversion(std::vector<llvm::Value*>& values,
492 llvm::Type* targetElementType,
493 llvm::IRBuilder<>& builder)
494{
495 assert(targetElementType && (targetElementType->isIntegerTy() ||
496 targetElementType->isFloatingPointTy()) &&
497 "Target element type is not a scalar type");
498
499 llvm::Type* sourceElementType = values.front()->getType();
500 assert(sourceElementType && (sourceElementType->isIntegerTy() ||
501 sourceElementType->isFloatingPointTy()) &&
502 "Source element type is not a scalar type");
503
504 if (sourceElementType == targetElementType) return;
505
506 CastFunction llvmCastFunction = llvmArithmeticConversion(sourceElementType, targetElementType);
507
508 for (llvm::Value*& value : values) {
509 value = llvmCastFunction(builder, value, targetElementType);
510 }
511}
512
513/// @brief Converts a vector of loaded llvm scalar values to the highest precision
514/// type stored amongst them. Any values which are not scalar types are ignored
515/// @warning This assumes any integer types are signed.
516/// @param values A vector of llvm scalar values to convert
517/// @param builder The current llvm IRBuilder
518inline void
519arithmeticConversion(std::vector<llvm::Value*>& values,
520 llvm::IRBuilder<>& builder)
521{
522 llvm::Type* typeCast = LLVMType<bool>::get(builder.getContext());
523 for (llvm::Value*& value : values) {
524 llvm::Type* type = value->getType();
525 if (type->isIntegerTy() || type->isFloatingPointTy()) {
526 typeCast = typePrecedence(typeCast, type);
527 }
528 }
529
530 arithmeticConversion(values, typeCast, builder);
531}
532
533/// @brief Chooses the highest order llvm Type as defined by typePrecedence
534/// from either of the two incoming values and casts the other value to
535/// the choosen type if it is not already. The types of valueA and valueB
536/// are guaranteed to match. Both values must be scalar LLVM types
537/// @warning This assumes any integer types are signed.
538/// @param valueA The first llvm value
539/// @param valueB The second llvm value
540/// @param builder The current llvm IRBuilder
541inline void
542arithmeticConversion(llvm::Value*& valueA,
543 llvm::Value*& valueB,
544 llvm::IRBuilder<>& builder)
545{
546 llvm::Type* type = typePrecedence(valueA->getType(), valueB->getType());
547 valueA = arithmeticConversion(valueA, type, builder);
548 valueB = arithmeticConversion(valueB, type, builder);
549}
550
551/// @brief Performs a C style boolean comparison from a given scalar LLVM value
552///
553/// @param value The scalar llvm value to convert to a boolean
554/// @param builder The current llvm IRBuilder
555///
556inline llvm::Value*
558 llvm::IRBuilder<>& builder)
559{
560 llvm::Type* type = value->getType();
561
562 if (type->isFloatingPointTy()) return builder.CreateFCmpONE(value, llvm::ConstantFP::get(type, 0.0));
563 else if (type->isIntegerTy(1)) return builder.CreateICmpNE(value, llvm::ConstantInt::get(type, 0));
564 else if (type->isIntegerTy()) return builder.CreateICmpNE(value, llvm::ConstantInt::getSigned(type, 0));
565 assert(false && "Invalid type for bool conversion");
566 return nullptr;
567}
568
569/// @ brief Performs a binary operation on two loaded llvm scalar values of the same type.
570/// The type of operation performed is defined by the token (see the list of supported
571/// tokens in ast/Tokens.h. Returns a loaded llvm scalar result
572///
573/// @param lhs The left hand side value of the binary operation
574/// @param rhs The right hand side value of the binary operation
575/// @param token The token representing the binary operation to perform
576/// @param builder The current llvm IRBuilder
577inline llvm::Value*
578binaryOperator(llvm::Value* lhs, llvm::Value* rhs,
579 const ast::tokens::OperatorToken& token,
580 llvm::IRBuilder<>& builder)
581{
582 llvm::Type* lhsType = lhs->getType();
583 assert(lhsType == rhs->getType() ||
584 (token == ast::tokens::SHIFTLEFT ||
585 token == ast::tokens::SHIFTRIGHT));
586
588
589 if (opType == ast::tokens::LOGICAL) {
590 lhs = boolComparison(lhs, builder);
591 rhs = boolComparison(rhs, builder);
592 lhsType = lhs->getType(); // now bool type
593 }
594
595 const BinaryFunction llvmBinaryFunction = llvmBinaryConversion(lhsType, token);
596 return llvmBinaryFunction(builder, lhs, rhs);
597}
598
599/// @brief Unpack a particular element of an array and return a pointer to that element
600/// The provided llvm Value is expected to be a pointer to an array
601///
602/// @param ptrToArray A llvm value which is a pointer to a llvm array
603/// @param index The index at which to access the array
604/// @param builder The current llvm IRBuilder
605///
606inline llvm::Value*
607arrayIndexUnpack(llvm::Value* ptrToArray,
608 const int16_t index,
609 llvm::IRBuilder<>& builder)
610{
611 return builder.CreateConstGEP2_64(ptrToArray, 0, index);
612}
613
614/// @brief Unpack an array type into llvm Values which represent all its elements
615/// The provided llvm Value is expected to be a pointer to an array
616/// If loadElements is true, values will store loaded llvm values instead
617/// of pointers to the array elements
618///
619/// @param ptrToArray A llvm value which is a pointer to a llvm array
620/// @param values A vector of llvm values where to store the array elements
621/// @param builder The current llvm IRBuilder
622/// @param loadElements Whether or not to load each array element into a register
623///
624inline void
625arrayUnpack(llvm::Value* ptrToArray,
626 std::vector<llvm::Value*>& values,
627 llvm::IRBuilder<>& builder,
628 const bool loadElements = false)
629{
630 const size_t elements =
631 ptrToArray->getType()->getContainedType(0)->getArrayNumElements();
632
633 values.reserve(elements);
634 for (size_t i = 0; i < elements; ++i) {
635 llvm::Value* value = builder.CreateConstGEP2_64(ptrToArray, 0, i);
636 if (loadElements) value = builder.CreateLoad(value);
637 values.push_back(value);
638 }
639}
640
641/// @brief Unpack the first three elements of an array.
642/// The provided llvm Value is expected to be a pointer to an array
643/// @note The elements are note loaded
644///
645/// @param ptrToArray A llvm value which is a pointer to a llvm array
646/// @param value1 The first array value
647/// @param value2 The second array value
648/// @param value3 The third array value
649/// @param builder The current llvm IRBuilder
650///
651inline void
652array3Unpack(llvm::Value* ptrToArray,
653 llvm::Value*& value1,
654 llvm::Value*& value2,
655 llvm::Value*& value3,
656 llvm::IRBuilder<>& builder)
657{
658 assert(ptrToArray && ptrToArray->getType()->isPointerTy() &&
659 "Input to array3Unpack is not a pointer type.");
660
661 value1 = builder.CreateConstGEP2_64(ptrToArray, 0, 0);
662 value2 = builder.CreateConstGEP2_64(ptrToArray, 0, 1);
663 value3 = builder.CreateConstGEP2_64(ptrToArray, 0, 2);
664}
665
666/// @brief Pack three values into a new array and return a pointer to the
667/// newly allocated array. If the values are of a mismatching type,
668/// the highets order type is uses, as defined by typePrecedence. All
669/// llvm values are expected to a be a loaded scalar type
670///
671/// @param value1 The first array value
672/// @param value2 The second array value
673/// @param value3 The third array value
674/// @param builder The current llvm IRBuilder
675///
676inline llvm::Value*
677array3Pack(llvm::Value* value1,
678 llvm::Value* value2,
679 llvm::Value* value3,
680 llvm::IRBuilder<>& builder)
681{
682 llvm::Type* type = typePrecedence(value1->getType(), value2->getType());
683 type = typePrecedence(type, value3->getType());
684
685 value1 = arithmeticConversion(value1, type, builder);
686 value2 = arithmeticConversion(value2, type, builder);
687 value3 = arithmeticConversion(value3, type, builder);
688
689 llvm::Type* vectorType = llvm::ArrayType::get(type, 3);
690 llvm::Value* vector = insertStaticAlloca(builder, vectorType);
691
692 llvm::Value* e1 = builder.CreateConstGEP2_64(vector, 0, 0);
693 llvm::Value* e2 = builder.CreateConstGEP2_64(vector, 0, 1);
694 llvm::Value* e3 = builder.CreateConstGEP2_64(vector, 0, 2);
695
696 builder.CreateStore(value1, e1);
697 builder.CreateStore(value2, e2);
698 builder.CreateStore(value3, e3);
699
700 return vector;
701}
702
703/// @brief Pack a loaded llvm scalar value into a new array of a specified
704/// size and return a pointer to the newly allocated array. Each element
705/// of the new array will have the value of the given scalar
706///
707/// @param value The uniform scalar llvm value to pack into the array
708/// @param builder The current llvm IRBuilder
709/// @param size The size of the newly allocated array
710///
711inline llvm::Value*
712arrayPack(llvm::Value* value,
713 llvm::IRBuilder<>& builder,
714 const size_t size = 3)
715{
716 assert(value && (value->getType()->isIntegerTy() ||
717 value->getType()->isFloatingPointTy()) &&
718 "value type is not a scalar type");
719
720 llvm::Type* type = value->getType();
721 llvm::Value* array =
722 insertStaticAlloca(builder,
723 llvm::ArrayType::get(type, size));
724
725 for (size_t i = 0; i < size; ++i) {
726 llvm::Value* element = builder.CreateConstGEP2_64(array, 0, i);
727 builder.CreateStore(value, element);
728 }
729
730 return array;
731}
732
733/// @brief Pack a vector of loaded llvm scalar values into a new array of
734/// equal size and return a pointer to the newly allocated array.
735///
736/// @param values A vector of loaded llvm scalar values to pack
737/// @param builder The current llvm IRBuilder
738///
739inline llvm::Value*
740arrayPack(const std::vector<llvm::Value*>& values,
741 llvm::IRBuilder<>& builder)
742{
743 llvm::Type* type = values.front()->getType();
744 llvm::Value* array = insertStaticAlloca(builder,
745 llvm::ArrayType::get(type, values.size()));
746
747 size_t idx = 0;
748 for (llvm::Value* const& value : values) {
749 llvm::Value* element = builder.CreateConstGEP2_64(array, 0, idx++);
750 builder.CreateStore(value, element);
751 }
752
753 return array;
754}
755
756/// @brief Pack a vector of loaded llvm scalar values into a new array of
757/// equal size and return a pointer to the newly allocated array.
758/// arrayPackCast first checks all the contained types in values
759/// and casts all types to the highest order type present. All llvm
760/// values in values are expected to be loaded scalar types
761///
762/// @param values A vector of loaded llvm scalar values to pack
763/// @param builder The current llvm IRBuilder
764///
765inline llvm::Value*
766arrayPackCast(std::vector<llvm::Value*>& values,
767 llvm::IRBuilder<>& builder)
768{
769 // get the highest order type present
770
771 llvm::Type* type = LLVMType<bool>::get(builder.getContext());
772 for (llvm::Value* const& value : values) {
773 type = typePrecedence(type, value->getType());
774 }
775
776 // convert all to this type
777
778 for (llvm::Value*& value : values) {
779 value = arithmeticConversion(value, type, builder);
780 }
781
782 return arrayPack(values, builder);
783}
784
785inline llvm::Value*
786scalarToMatrix(llvm::Value* scalar,
787 llvm::IRBuilder<>& builder,
788 const size_t dim = 3)
789{
790 assert(scalar && (scalar->getType()->isIntegerTy() ||
791 scalar->getType()->isFloatingPointTy()) &&
792 "value type is not a scalar type");
793
794 llvm::Type* type = scalar->getType();
795 llvm::Value* array =
796 insertStaticAlloca(builder,
797 llvm::ArrayType::get(type, dim*dim));
798
799 llvm::Value* zero = llvmConstant(0, type);
800 for (size_t i = 0; i < dim*dim; ++i) {
801 llvm::Value* m = ((i % (dim+1) == 0) ? scalar : zero);
802 llvm::Value* element = builder.CreateConstGEP2_64(array, 0, i);
803 builder.CreateStore(m, element);
804 }
805
806 return array;
807}
808
809} // namespace codegen
810} // namespace ax
811} // namespace OPENVDB_VERSION_NAME
812} // namespace openvdb
813
814#endif // OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
815
ValueT value
Definition: GridBuilder.h:1287
Consolidated llvm types for most supported types.
@ BITOR
Definition: axparser.h:137
@ DIVIDE
Definition: axparser.h:151
@ LESSTHANOREQUAL
Definition: axparser.h:145
@ SHIFTRIGHT
Definition: axparser.h:147
@ MORETHANOREQUAL
Definition: axparser.h:144
@ EQUALSEQUALS
Definition: axparser.h:140
@ BITXOR
Definition: axparser.h:138
@ BITAND
Definition: axparser.h:139
@ AND
Definition: axparser.h:136
@ PLUS
Definition: axparser.h:148
@ LESSTHAN
Definition: axparser.h:143
@ OR
Definition: axparser.h:135
@ MODULO
Definition: axparser.h:152
@ MORETHAN
Definition: axparser.h:142
@ NOTEQUALS
Definition: axparser.h:141
@ MULTIPLY
Definition: axparser.h:150
@ SHIFTLEFT
Definition: axparser.h:146
@ MINUS
Definition: axparser.h:149
OperatorToken
Definition: Tokens.h:151
OperatorType
Definition: Tokens.h:201
@ LOGICAL
Definition: Tokens.h:203
@ BITWISE
Definition: Tokens.h:205
OperatorType operatorType(const OperatorToken token)
Definition: Tokens.h:210
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:542
void llvmTypeToString(const llvm::Type *const type, std::string &str)
Prints an llvm type to a std string.
Definition: Utils.h:70
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
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:766
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:443
llvm::Constant * llvmConstant(const T t, llvm::Type *type)
Returns an llvm Constant holding a scalar value.
Definition: Types.h:328
llvm::Value * scalarToMatrix(llvm::Value *scalar, llvm::IRBuilder<> &builder, const size_t dim=3)
Definition: Utils.h:786
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Type *)> CastFunction
Definition: Utils.h:44
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:740
llvm::Value * boolComparison(llvm::Value *value, llvm::IRBuilder<> &builder)
Performs a C style boolean comparison from a given scalar LLVM value.
Definition: Utils.h:557
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:330
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:625
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:607
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:652
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
llvm::Value * binaryOperator(llvm::Value *lhs, llvm::Value *rhs, const ast::tokens::OperatorToken &token, llvm::IRBuilder<> &builder)
Definition: Utils.h:578
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
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 * 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
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Value *)> BinaryFunction
Definition: Utils.h:47
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
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....
Definition: Utils.h:677
bool isValidCast(llvm::Type *from, llvm::Type *to)
Returns true if the llvm Type 'from' can be safely cast to the llvm Type 'to'.
Definition: Utils.h:390
llvm::Argument * extractArgument(llvm::Function *F, const std::string &name)
Definition: Utils.h:163
Definition: Exceptions.h:13
#define BIND_ARITHMETIC_CAST_OP(Function, Twine)
#define BIND_BINARY_OP(Function)
LLVM type mapping from pod types.
Definition: Types.h:55
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202