OpenVDB 12.1.0
Loading...
Searching...
No Matches
Value.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/Value.h
5///
6/// @authors Nick Avramoussis
7///
8/// @brief Intermediate representation of supported AX values
9///
10
11#ifndef OPENVDB_AX_CODEGEN_VALUE_HAS_BEEN_INCLUDED
12#define OPENVDB_AX_CODEGEN_VALUE_HAS_BEEN_INCLUDED
13
14#include "Types.h"
15#include "Utils.h"
16
17#include "../ast/AST.h"
18#include "../ast/Tokens.h"
19#include "../compiler/Logger.h"
20
21#include <openvdb/version.h>
22#include <openvdb/util/Assert.h>
23
24#include <llvm/IR/IRBuilder.h>
25#include <llvm/IR/LLVMContext.h>
26
27namespace openvdb {
29namespace OPENVDB_VERSION_NAME {
30
31namespace ax {
32namespace codegen {
33
34/// @brief Intermediate representation wrapper for supported value types in AX
35/// as immutable instances.
36///@details This class allows you to generate LLVM IR for common operations
37/// supported by the AX grammar whilst abstracting away a lot of the
38/// complexity of the underlying LLVM state. This class is not meant as a
39/// definitive representation of all possible LLVM IR instructions that can
40/// be emitted, more as a strict helper translation layer for some supported
41/// AX instructions to LLVM IR. More generally, this is intended to be used
42/// for all arithmetic operations and less so for program control flow
43/// (branches, loops, etc). Importantly, this class abstracts away the
44/// concept of ptr/loaded instructions. That is, users of this API usually do
45/// not need to worry about explicitly loading or querying the state of LLVM
46/// IR allocations when calling methods. Instances of Value types also ensure
47/// that the required underlying type information is retained, necessary from
48/// LLVM 15 onwards (due to the introduction of LLVM's opaque ptr changes).
49///
50/// The subset of possible types this class supports are
51/// - void types (for function returns)
52/// - AX scalar values (bool, ints, floats)
53/// - AX array values (element types of ints or floats)
54/// - AX string values
55///
56/// Note that a Value can have a variety of arithmetic type/precision states.
57/// The API of this class does not guaranteed compatibility between all
58/// states. Some failure cases may report a log message and return
59/// Value::Invalid() or generate invalid IR/result in undefined behaviour if
60/// the inputs are not correct. Refer to individual API methods for more
61/// details.
63{
64public:
65 /// @brief Create a Value with a provided underlying type
66 /// @warning This constructor assumes that the underlying type is correct.
67 /// This cannot be asserted from LLVM 15 onwards. This should be used
68 /// sparingly and currently only exists to support some holes in the
69 /// compute generator. This constructor should eventually be removed as
70 /// these get closed, in favour of the static create/alloc methods.
71 Value(llvm::Value* val, llvm::Type* utype)
72 : mVal(val)
73 , mUType(utype) {
74 OPENVDB_ASSERT((!mVal && !mUType) || bool(*this));
75 }
76 /// @brief Initialize from a constant value
77 explicit Value(llvm::Constant* costant)
78 : Value(static_cast<llvm::Value*>(costant), costant->getType()) {}
79
80 Value(const Value&) = default;
81 Value(Value&&) = default;
82 Value& operator=(const Value&) = default;
83 Value& operator=(Value&&) = default;
84
85 /// @brief Return an invalid Value. This is used to represent various fail
86 /// cases. Note that the operator bool(Value) will return false in for
87 /// Value::Invalid
88 static Value Invalid() { return Value(nullptr, nullptr); }
89
90 /// @brief Create an arithmetic literal
91 template <typename ValueType>
92 static Value Create(llvm::LLVMContext& C, const ValueType& value)
93 {
94 return Value(LLVMType<ValueType>::get(C, value),
96 }
97
98 /// @brief Create a value that represents a return value from a function.
99 /// Really only intended to be used by the function framework. If ret is
100 /// not provided, a void return is created.
101 static Value Return(llvm::IRBuilder<>& B, Value* ret = nullptr)
102 {
103 return ret ? Value(B.CreateRet(ret->GetValue()), B.getVoidTy()) :
104 Value(B.CreateRetVoid(), B.getVoidTy());
105 }
106
107 /// @brief Return true if the underlying type held by utype is supported
108 /// via the interface of this Value class
109 static bool Supports(llvm::Type* utype)
110 {
111 llvm::Type* strtype = LLVMType<codegen::String>::get(utype->getContext());
112 return utype->isVoidTy() ||
113 utype->isIntegerTy() ||
114 utype->isFloatingPointTy() ||
115 utype == strtype ||
116 (utype->isArrayTy() &&
117 (utype->getArrayElementType()->isIntegerTy() ||
118 utype->getArrayElementType()->isFloatingPointTy()));
119 }
120
121 /// @brief Emit IR inserting an allocation at the front of the BasicBlock
122 /// pointed to by the provided IRBuilder. The type is expected to be a
123 /// supported utype.
124 static Value Alloc(llvm::IRBuilder<>& B, llvm::Type* type, llvm::Value* size = nullptr)
125 {
127 // Create the allocation at the start of the function block
128 llvm::Function* parent = B.GetInsertBlock()->getParent();
129 OPENVDB_ASSERT(parent && !parent->empty());
130 auto IP = B.saveIP();
131 llvm::BasicBlock& block = parent->front();
132 if (block.empty()) B.SetInsertPoint(&block);
133 else B.SetInsertPoint(&(block.front()));
134 llvm::Value* result = B.CreateAlloca(type, size);
135
136 /// @note Strings need to be initialised correctly when they are
137 /// created. We alloc them at the start of the function but
138 /// strings in branches may not ever be set to anything. If
139 /// we don't init these correctly, the clearup frees will
140 /// try and free uninitialised memory
141 llvm::StructType* strtype = LLVMType<codegen::String>::get(B.getContext());
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 OPENVDB_ASSERT(AssertOpaquePtrs(sso, strtype->getTypeAtIndex(1)));
146 llvm::Value* sso_load = B.CreateConstInBoundsGEP2_64(strtype->getTypeAtIndex(1), sso, 0, 0); // char[]
147 llvm::Value* len = B.CreateStructGEP(strtype, result, 2);
148 B.CreateStore(sso_load, cptr); // this->ptr = this->SSO;
149 B.CreateStore(B.getInt64(0), len);
150 }
151 B.restoreIP(IP);
152 return Value(result, type);
153 }
154
155 /// @brief Emit IR to create an array from a set of scalar values. Will
156 /// generate invalid IR if the values are not all scalar or are of
157 /// different precision. values cannot be empty.
158 static Value ScalarsToArray(llvm::IRBuilder<>& B, const std::vector<Value>& values)
159 {
160 OPENVDB_ASSERT(!values.empty());
161
162 llvm::Type* type = values.front().GetUnderlyingType();
163 llvm::Type* arrayType = llvm::ArrayType::get(type, values.size());
164 Value array = Value::Alloc(B, arrayType);
165
166 size_t idx = 0;
167 for (const Value& value : values)
168 {
169 OPENVDB_ASSERT(value);
170 OPENVDB_ASSERT(value.IsScalar());
171 OPENVDB_ASSERT(value.GetUnderlyingType() == type);
172 Value element = array.GetArrayElement(B, idx++);
173 B.CreateStore(value.GetValue(), element.GetValue());
174 }
175
176 return array;
177 }
178
179 /// @brief Create a new zero scalar Value using the underlying scalar
180 /// precision of this Value. Does not generate IR, however will return
181 /// an invalid constant if this Value is a string
182 Value Zero() const
183 {
185 }
186
187 /// @brief Create a new one scalar Value using the underlying scalar
188 /// precision of this Value. Does not generate IR, however will return
189 /// an invalid constant if this Value is a string
190 Value One() const
191 {
193 }
194
195 ///////////////////////////////////////////////////////////////////////////
196 ///////////////////////////////////////////////////////////////////////////
197
198 /// @brief Check if this Value contains an active underlying llvm
199 /// Value/Type. When asserts are enabled. This method strictly checks all
200 /// possible valid combination types of a Value.
201 operator bool() const;
202
203 /// @brief See bool operator
204 bool operator!() const { return !bool(*this); }
205
206 /// @brief Return true if this value represents a void type. This is
207 /// typically only possible for void function returns
208 /// @note Void types are only ever explicitly void, never pointers to void
209 bool IsVoid() const
210 {
211 OPENVDB_ASSERT(*this);
212 return mUType->isVoidTy();
213 }
214
215 /// @brief Return true if the underlying type is a bool type
216 /// @note A bool's underlying state can be either as a pointer or
217 /// loaded. This method returns true in both instances if the underlying
218 /// type is a bool.
219 bool IsBool() const
220 {
221 OPENVDB_ASSERT(*this);
222 return mUType->isIntegerTy(1);
223 }
224
225 /// @brief Return true if the underlying type is a scalar type (bool, int
226 /// or float).
227 /// @note A scalar's underlying state can be either as a pointer or
228 /// loaded. This method returns true in both instances if the underlying
229 /// type is a scalar.
230 bool IsScalar() const
231 {
232 OPENVDB_ASSERT(*this);
233 return (mUType->isIntegerTy() || mUType->isFloatingPointTy());
234 }
235
236 /// @brief Return true if the underlying type is an integer type
237 /// @note A integer's underlying state can be either as a pointer or
238 /// loaded. This method returns true in both instances if the underlying
239 /// type is a integer.
240 bool IsInteger() const
241 {
242 OPENVDB_ASSERT(*this);
243 return mUType->isIntegerTy();
244 }
245
246 /// @brief Return true if the underlying type is an floating point type
247 /// (float or double).
248 /// @note A float's underlying state can be either as a pointer or
249 /// loaded. This method returns true in both instances if the underlying
250 /// type is a float/double.
251 bool IsFloat() const
252 {
253 OPENVDB_ASSERT(*this);
254 return mUType->isFloatingPointTy();
255 }
256
257 /// @brief Return true if the underlying type is an array type
258 /// @note An array type's state is only ever a pointer to an array
259 /// allocation
260 bool IsArray() const
261 {
262 OPENVDB_ASSERT(*this);
263 return mUType->isArrayTy();
264 }
265
266 /// @brief Return true if the underlying type is an vector 2/3/4 type
267 /// @note An vector type's state is only ever a pointer to a vector
268 /// allocation
269 bool IsVector() const
270 {
271 OPENVDB_ASSERT(*this);
272 return mUType->isArrayTy() && !this->IsMatrix();
273 }
274
275 /// @brief Return true if the underlying type is an matrix 3/4 type
276 /// @note An matrix type's state is only ever a pointer to a matrix
277 /// allocation
278 bool IsMatrix() const
279 {
280 OPENVDB_ASSERT(*this);
281 return mUType->isArrayTy() &&
282 // @todo This is dumb, add type-metadata for this
283 (this->GetArrayNumElements() == 9 || this->GetArrayNumElements() == 16);
284 }
285
286 /// @brief Return true if the underlying type is a string type
287 /// @note An string type's state is only ever a pointer to a string
288 /// allocation
289 bool IsString() const
290 {
291 OPENVDB_ASSERT(*this);
292 return mUType == LLVMType<codegen::String>::get(mUType->getContext());
293 }
294
295 /// @brief Return true if this Value is a pointer type
296 bool IsPtr() const
297 {
298 OPENVDB_ASSERT(*this);
299 return mVal->getType()->isPointerTy();
300 }
301
302 /// @brief Return true if this Value is a constant
303 bool IsConstant() const
304 {
305 return bool(llvm::dyn_cast<llvm::Constant>(this->GetValue()));
306 }
307
308 /// @brief Return the number of elements in this array type
309 size_t GetArrayNumElements() const
310 {
311 OPENVDB_ASSERT(this->IsArray());
312 return mUType->getArrayNumElements();
313 }
314
315 ///////////////////////////////////////////////////////////////////////////
316 ///////////////////////////////////////////////////////////////////////////
317
318 // The following method generate IR and return new Value instances
319
320 /// @brief Emit IR to check whether this value is NaN. Only works on Float
321 /// types and will generate invalid IR if this Value is not a Float
322 /// instance.
323 Value IsNan(llvm::IRBuilder<>& B) const
324 {
325 OPENVDB_ASSERT(*this);
326 OPENVDB_ASSERT(this->IsFloat());
327 Value self = this->LoadIfPtr(B);
328 llvm::Value* result = B.CreateFCmpUNO(self.GetValue(), self.GetValue());
329 return Value(result, result->getType());
330 }
331
332 /// @brief Emit IR to load the current value. Not typically required to
333 /// call directly. Will generate invalid IR if this Value is not a Ptr.
334 /// @warning Should only ever be called for ptrs to scalar types
335 Value Load(llvm::IRBuilder<>& B) const
336 {
337 OPENVDB_ASSERT(*this);
338 OPENVDB_ASSERT(this->IsPtr());
339 OPENVDB_ASSERT(this->IsScalar()); // Should only be loading scalars
340 return Value(B.CreateLoad(mUType, mVal), mUType);
341 }
342
343 /// @brief Emit IR to load the current value if it is a ptr. Not typically
344 /// required to call directly.
345 /// @warning Should only ever be called for ptrs to scalar types
346 Value LoadIfPtr(llvm::IRBuilder<>& B) const
347 {
348 OPENVDB_ASSERT(*this);
349 OPENVDB_ASSERT(this->IsScalar()); // Should only be loading scalars
350 return this->IsPtr() ? this->Load(B) : *this;
351 }
352
353 /// @brief Emit IR to return a scalar at the provided index from this
354 /// array value. Will generate invalid IR if this Value is not an array
355 /// or if idx is not an integer.
356 Value GetArrayElement(llvm::IRBuilder<>& B, Value idx) const
357 {
358 OPENVDB_ASSERT(*this);
359 OPENVDB_ASSERT(this->IsArray());
361 idx = idx.LoadIfPtr(B);
362 if (this->IsPtr()) {
363 Value zero = idx.Zero(); // same int precision as idx
364 return Value(B.CreateInBoundsGEP(mUType, mVal, {zero.GetValue(), idx.GetValue()}), mUType->getArrayElementType());
365 }
366 else {
367 OPENVDB_ASSERT(this->IsConstant());
368 return Value(B.CreateInBoundsGEP(mUType, mVal, {idx.GetValue()}), mUType->getArrayElementType());
369 }
370 }
371
372 /// @brief Emit IR to return a scalar at the provided index from this
373 /// array value. Will generate invalid IR if this Value is not an array.
374 Value GetArrayElement(llvm::IRBuilder<>& B, uint64_t idx) const
375 {
376 OPENVDB_ASSERT(*this);
377 OPENVDB_ASSERT(this->IsArray());
378 OPENVDB_ASSERT(idx < this->GetArrayNumElements());
379 if (this->IsPtr()) {
380 return Value(B.CreateConstInBoundsGEP2_64(mUType, mVal, uint64_t(0), idx), mUType->getArrayElementType());
381 }
382 else {
383 OPENVDB_ASSERT(this->IsConstant());
384 return Value(B.CreateConstInBoundsGEP1_64(mUType, mVal, idx), mUType->getArrayElementType());
385 }
386 }
387
388 /// @brief Emit IR to extract scalar values from the elements in this
389 /// array and populate the provided vector with them. The scalars are
390 /// additionally loaded if load is true. Will generate invalid IR if this
391 /// is not an array
392 void ArrayToScalars(llvm::IRBuilder<>& B,
393 std::vector<Value>& elements,
394 const bool load = false) const
395 {
396 OPENVDB_ASSERT(*this);
397 OPENVDB_ASSERT(this->IsArray());
398
399 const size_t size = this->GetArrayNumElements();
400 elements.reserve(size);
401
402 for (size_t i = 0; i < size; ++i)
403 {
404 Value elem = this->GetArrayElement(B, i);
405 if (load) elem = elem.Load(B);
406 OPENVDB_ASSERT(elem);
407 elements.emplace_back(elem);
408 }
409
410 OPENVDB_ASSERT(!elements.empty());
411 }
412
413 /// @brief Emit IR to broadcast this scalar to a new array. Will generated
414 /// invalid IR if this is not a scalar or if size is zero.
415 /// @warning This fills the array with the current scalar value. It does
416 /// NOT do scalar->matrix promotion.
417 Value ScalarToArray(llvm::IRBuilder<>& B, size_t size) const
418 {
419 OPENVDB_ASSERT(*this);
420 OPENVDB_ASSERT_MESSAGE(this->IsScalar(), "value type is not a scalar type");
421 Value scalar = this->LoadIfPtr(B);
422
423 llvm::Type* type = llvm::ArrayType::get(scalar.GetUnderlyingType(), size);
424 Value array = Value::Alloc(B, type);
425 for (size_t i = 0; i < size; ++i) {
426 Value element = array.GetArrayElement(B, i);
427 B.CreateStore(scalar.GetValue(), element.GetValue());
428 }
429 return array;
430 }
431
432 /// @brief Emit IR to create a new 3x3 matrix from this scalar value,
433 /// adhering to scalar->matrix promotion rules. Will generate invalid IR
434 /// if this is not a scalar value.
435 Value ScalarToIdentMatrix3(llvm::IRBuilder<>& B) const
436 {
437 return this->ScalarToIdentMatrixN<3>(B);
438 }
439
440 /// @brief Emit IR to create a new 4x4 matrix from this scalar value,
441 /// adhering to scalar->matrix promotion rules. Will generate invalid IR
442 /// if this is not a scalar value.
443 Value ScalarToIdentMatrix4(llvm::IRBuilder<>& B) const
444 {
445 return this->ScalarToIdentMatrixN<4>(B);
446 }
447
448 /// @brief Emit IR to perform standard boolean comparison on this scalar
449 /// i.e. bool(scalar) or bool(scalar == 0). Returns a Value of type bool.
450 /// Will return Value::Invalid() if this is not a bool/int/float.
451 Value ScalarBoolComparison(llvm::IRBuilder<>& B) const;
452
453 /// @brief Emit IR to select a value based on this boolean scalar value.
454 /// Will generate invalid IR if this is not a boolean value, or if
455 /// trueval and falseval have different types.
456 Value Select(llvm::IRBuilder<>& B, const Value& trueval, const Value& falseval) const;
457
458 /// @brief Emit IR to cast this scalar or array to a new value of the
459 /// provided scalar precision. Returns a new value of the same class type
460 /// (scalar or array) but with a new precision. Will generate invalid IR
461 /// if the precision is not an integer or floating point precision type.
462 /// If this is not an array or scalar, no IR is emitted and
463 /// Value::Invalid() is returned. Additionally, if the provided precision
464 /// is the same as this scalar/array's underlying precision, no IR is
465 /// emitted and no new value is created.
466 Value CastToPrecision(llvm::IRBuilder<>& B, llvm::Type* precision) const;
467
468 ///////////////////////////////////////////////////////////////////////////
469 ///////////////////////////////////////////////////////////////////////////
470
471 // Unary IR emission
472
473 /// @brief Emit IR to create a unary not instruction on this scalar or
474 /// integer array (i.e. !value). If the value is not a int/float/integer
475 /// array, no IR is emitted, a warning is logged (if a logger is provided)
476 /// and Value::Invalid is returned.
477 Value Not(llvm::IRBuilder<>& B, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
478
479 /// @brief Emit IR to create a unary not instruction on this integer or
480 /// integer array (i.e. ~value). If the value is not a int/integer array,
481 /// no IR is emitted, a warning is logged (if a logger is provided) and
482 /// Value::Invalid is returned.
483 Value BitNot(llvm::IRBuilder<>& B, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
484
485 /// @brief Emit IR to create a unary not instruction on this scalar or
486 /// array (i.e. -value). If the value is not a int/float/array, no IR is
487 /// is emitted, a warning is logged (if a logger is provided) and
488 /// Value::Invalid is returned.
489 Value Negate(llvm::IRBuilder<>& B, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
490
491 /// @brief Emit IR to increment this scalar (i.e. value + 1). Will
492 /// return Value::Invalid() and report a message to a logger (if provided)
493 /// if this is not an integer (non-bool) or float scalar.
494 Value Increment(llvm::IRBuilder<>& B, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
495
496 /// @brief Emit IR to increment this scalar (i.e. value - 1). Will
497 /// return Value::Invalid() and report a message to a logger (if provided)
498 /// if this is not an integer (non-bool) or float scalar.
499 Value Decrement(llvm::IRBuilder<>& B, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
500
501 ///////////////////////////////////////////////////////////////////////////
502 ///////////////////////////////////////////////////////////////////////////
503
504 /// @brief Emit IR to perform a binary operation on this LHS value and a
505 /// provided RHS value. If the operation is not a valid binary operation,
506 /// Value::Invalid() is returned. Defer to the explicit binary methods on
507 /// this class for more details.
508 /// @note For all methods, performing type promotion/casting where necessary
509 /// or return Value::Invalid() on incompatible inputs and report a
510 /// message to the log, if provided.
511 Value Binary(llvm::IRBuilder<>& B,
512 Value rhs,
514 Logger* log = nullptr,
515 const ax::ast::Node* node = nullptr) const
516 {
517 OPENVDB_ASSERT(*this);
518 OPENVDB_ASSERT(rhs);
519 switch (op)
520 {
521 case ast::tokens::OperatorToken::PLUS : return this->Add(B, rhs, log, node);
522 case ast::tokens::OperatorToken::MINUS : return this->Subtract(B, rhs, log, node);
523 case ast::tokens::OperatorToken::MULTIPLY : return this->Multiply(B, rhs, log, node);
524 case ast::tokens::OperatorToken::DIVIDE : return this->Divide(B, rhs, log, node);
525 case ast::tokens::OperatorToken::MODULO : return this->Modulo(B, rhs, log, node);
526 case ast::tokens::OperatorToken::EQUALSEQUALS : return this->Equals(B, rhs, log, node);
527 case ast::tokens::OperatorToken::NOTEQUALS : return this->NotEquals(B, rhs, log, node);
528 case ast::tokens::OperatorToken::MORETHAN : return this->GreaterThan(B, rhs, log, node);
529 case ast::tokens::OperatorToken::MORETHANOREQUAL : return this->GreaterThanEquals(B, rhs, log, node);
530 case ast::tokens::OperatorToken::LESSTHAN : return this->LessThan(B, rhs, log, node);
531 case ast::tokens::OperatorToken::LESSTHANOREQUAL : return this->LessThanEquals(B, rhs, log, node);
532 case ast::tokens::OperatorToken::SHIFTLEFT : return this->ShiftLeft(B, rhs, log, node);
533 case ast::tokens::OperatorToken::SHIFTRIGHT : return this->ShiftRight(B, rhs, log, node);
534 case ast::tokens::OperatorToken::BITAND : return this->BitAnd(B, rhs, log, node);
535 case ast::tokens::OperatorToken::BITOR : return this->BitOr(B, rhs, log, node);
536 case ast::tokens::OperatorToken::BITXOR : return this->BitXor(B, rhs, log, node);
537 default: return Value::Invalid();
538 }
539 }
540
541 // Binary arithmetic IR emission.
542
543 /// @brief Emit IR to perform a && operation on two scalars. Assumes both
544 /// inputs are scalars (this checking is currently done in the
545 /// ComputeGenerator) and will cause undefined behaviour if they are not.
546 /// @warning This does not perform short circuiting. See:
547 /// ComputeGenerator::visit(const ast::BinaryOperator*)
548 Value And(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
549 {
550 OPENVDB_ASSERT(*this);
551 OPENVDB_ASSERT(rhs);
552 OPENVDB_ASSERT(this->IsScalar());
554 Value lhs = *this;
555 lhs = lhs.ScalarBoolComparison(B);
556 rhs = rhs.ScalarBoolComparison(B);
557 return this->TrivialBinary(B, rhs, ast::tokens::AND, log, node);
558 }
559
560 /// @brief Emit IR to perform a || operation on two scalars. Assumes both
561 /// inputs are scalars (this checking is currently done in the
562 /// ComputeGenerator) and will cause undefined behaviour if they are not.
563 /// @warning This does not perform short circuiting. See:
564 /// ComputeGenerator::visit(const ast::BinaryOperator*)
565 Value Or(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
566 {
567 OPENVDB_ASSERT(*this);
568 OPENVDB_ASSERT(rhs);
569 OPENVDB_ASSERT(this->IsScalar());
571 Value lhs = *this;
572 lhs = lhs.ScalarBoolComparison(B);
573 rhs = rhs.ScalarBoolComparison(B);
574 return this->TrivialBinary(B, rhs, ast::tokens::OR, log, node);
575 }
576
577 /// @brief Emit IR to perform a + operation on two values
578 Value Add(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
579
580 /// @brief Emit IR to perform a - operation on two values
581 Value Subtract(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
582
583 /// @brief Emit IR to perform a * operation on two values
584 Value Multiply(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
585
586 /// @brief Emit IR to perform a / operation on two values If the denominator is constant and
587 /// zero, returns Value::Invalid()
588 Value Divide(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
589
590 /// @brief Emit IR to perform a FLOORED % operation on two values
591 Value Modulo(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
592
593 /// @brief Emit IR to perform a TRUNCATED % operation on two values
594 Value TruncatedModulo(llvm::IRBuilder<>& B, Value rhs) const;
595
596 // Binary Relational IR emission
597
598 /// @brief Emit IR to perform a == operation on two values
599 Value Equals(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
600
601 /// @brief Emit IR to perform a != operation on two values
602 Value NotEquals(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
603
604 /// @brief Emit IR to perform a > operation on two values
605 Value GreaterThan(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
606 {
607 return this->Relational(B, rhs, ast::tokens::MORETHAN, log, node);
608 }
609
610 /// @brief Emit IR to perform a >= operation on two values
611 Value GreaterThanEquals(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
612 {
613 return this->Relational(B, rhs, ast::tokens::MORETHANOREQUAL, log, node);
614 }
615
616 /// @brief Emit IR to perform a < operation on two values
617 Value LessThan(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
618 {
619 return this->Relational(B, rhs, ast::tokens::LESSTHAN, log, node);
620 }
621
622 /// @brief Emit IR to perform a <= operation on two values
623 Value LessThanEquals(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
624 {
625 return this->Relational(B, rhs, ast::tokens::LESSTHANOREQUAL, log, node);
626 }
627
628 // Binary Bitwise IR emission
629
630 /// @brief Emit IR to perform a << operation. Both values must be integers
631 Value ShiftLeft(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
632 {
633 return this->Bitwise(B, rhs, ast::tokens::SHIFTLEFT, log, node);
634 }
635
636 /// @brief Emit IR to perform a >> operation. Both values must be integers
637 Value ShiftRight(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
638 {
639 return this->Bitwise(B, rhs, ast::tokens::SHIFTRIGHT, log, node);
640 }
641
642 /// @brief Emit IR to perform a & operation. Both values must be integers
643 Value BitAnd(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
644 {
645 return this->Bitwise(B, rhs, ast::tokens::BITAND, log, node);
646 }
647
648 /// @brief Emit IR to perform a | operation. Both values must be integers
649 Value BitOr(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
650 {
651 return this->Bitwise(B, rhs, ast::tokens::BITOR, log, node);
652 }
653
654 /// @brief Emit IR to perform a ^ operation. Both values must be integers
655 Value BitXor(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const
656 {
657 return this->Bitwise(B, rhs, ast::tokens::BITXOR, log, node);
658 }
659
660 ///////////////////////////////////////////////////////////////////////////
661 ///////////////////////////////////////////////////////////////////////////
662
663 /// @brief Emit IR to assign the provided rhs to this value.
664 Value Assign(llvm::IRBuilder<>& B, Value rhs, Logger* log = nullptr, const ax::ast::Node* node = nullptr) const;
665
666 ///////////////////////////////////////////////////////////////////////////
667 ///////////////////////////////////////////////////////////////////////////
668
669 /// @brief Dump this value/type to llvm::errs
670 void Print() const;
671
672 /// @brief Access the underlying llvm Value
673 llvm::Value* GetValue() const { return mVal; }
674
675 /// @brief Access the underlying llvm Type
676 llvm::Type* GetUnderlyingType() const { return mUType; }
677
678 /// @brief Access the underlying scalar type. This method assumes the
679 /// current value is a scalar or array
680 llvm::Type* GetUnderlyingScalarType() const
681 {
682 OPENVDB_ASSERT(*this);
683 OPENVDB_ASSERT(this->IsScalar() || this->IsArray());
684 return mUType->isArrayTy() ? mUType->getArrayElementType() : mUType;
685 }
686
687private:
688 template <size_t Dim>
689 Value ScalarToIdentMatrixN(llvm::IRBuilder<>& B) const
690 {
691 static_assert(Dim == 3 || Dim == 4);
692 OPENVDB_ASSERT(*this);
693 OPENVDB_ASSERT_MESSAGE(this->IsScalar(), "value type is not a scalar type");
694
695 Value scalar = this->LoadIfPtr(B);
696 llvm::Type* type = llvm::ArrayType::get(scalar.GetUnderlyingType(), Dim*Dim);
697 Value array = Value::Alloc(B, type);
698 Value zero = scalar.Zero();
699
700 for (size_t i = 0; i < Dim*Dim; ++i) {
701 const Value& m = ((i % (Dim+1) == 0) ? scalar : zero);
702 llvm::Value* element = array.GetArrayElement(B, i).GetValue();
703 B.CreateStore(m.GetValue(), element);
704 }
705
706 return array;
707 }
708
709 Value TrivialBinary(llvm::IRBuilder<>& B,
710 Value rhs,
711 const ast::tokens::OperatorToken& op,
712 Logger* log,
713 const ax::ast::Node* node) const;
714
715 Value Bitwise(llvm::IRBuilder<>& B,
716 Value rhs,
717 const ast::tokens::OperatorToken& op,
718 Logger* log,
719 const ax::ast::Node* node) const;
720
721 Value Relational(llvm::IRBuilder<>& B,
722 Value rhs,
723 const ast::tokens::OperatorToken& op,
724 Logger* log,
725 const ax::ast::Node* node) const;
726
727
728 static Value Reduce(llvm::IRBuilder<>& B,
729 const std::vector<Value>& bools,
730 const ast::tokens::OperatorToken& op)
731 {
732 OPENVDB_ASSERT(!bools.empty());
733 OPENVDB_ASSERT(op == ast::tokens::AND || op == ast::tokens::OR);
734 Value result = bools.front();
735 OPENVDB_ASSERT(result.IsBool());
736 for (size_t i = 1; i < bools.size(); ++i) {
737 result = result.TrivialBinary(B, bools[i], op, nullptr, nullptr);
738 }
739 return result;
740 }
741
742 Value ReduceBoolArray(llvm::IRBuilder<>& B, const ast::tokens::OperatorToken& op) const
743 {
744 OPENVDB_ASSERT(this->IsArray());
745 OPENVDB_ASSERT(this->GetUnderlyingScalarType() == LLVMType<bool>::get(B.getContext()));
746 std::vector<Value> elements;
747 this->ArrayToScalars(B, elements);
748 return Value::Reduce(B, elements, op);
749 }
750
751 static bool WImplicitScalarToMatrix(Logger* log, const ax::ast::Node* node)
752 {
753 if (!node) return true;
754 if (auto* child = node->child(0)) {
755 if (child->isType<ast::ArrayPack>()) {
756 if (log) log->error("unable to deduce implicit {...} type for binary op as value "
757 "may be a matrix or array. assign to a local mat variable", child);
758 return false;
759 }
760 }
761 if (log && !log->warning("implicit cast to matrix from scalar. resulting "
762 "cast will be equal to scalar <op> identity.", node->child(1))) return false;
763 return true;
764 }
765
766 static Value WUnsupportedOp(const ast::tokens::CoreType& type,
767 const ast::tokens::OperatorToken& op,
768 Logger* log,
769 const ax::ast::Node* node)
770 {
771 const std::string typestr = ast::tokens::typeStringFromToken(type);
772 return Value::WUnsupportedOp(typestr, op, log, node);
773 }
774
775 static Value WUnsupportedOp(const std::string& typestr,
776 const ast::tokens::OperatorToken& op,
777 Logger* log,
778 const ax::ast::Node* node)
779 {
780 if (log) {
781 const std::string opstr = ast::tokens::operatorNameFromToken(op);
782 log->error("unsupported " + typestr + " operation \"" + opstr + "\"", node);
783 }
784 return Value::Invalid();
785 }
786
787private:
788 llvm::Value* mVal {nullptr};
789 llvm::Type* mUType {nullptr};
790};
791
792} // namespace codegen
793} // namespace ax
794} // namespace OPENVDB_VERSION_NAME
795} // namespace openvdb
796
797#endif // OPENVDB_AX_CODEGEN_VALUE_HAS_BEEN_INCLUDED
798
Provides the definition for every abstract and concrete derived class which represent a particular ab...
#define OPENVDB_ASSERT_MESSAGE(X, MSG)
Definition Assert.h:42
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
Logging system to collect errors and warnings throughout the different stages of parsing and compilat...
#define OPENVDB_AX_API
Definition Platform.h:312
Various function and operator tokens used throughout the AST and code generation.
Consolidated llvm types for most supported types.
Logger for collecting errors and warnings that occur during AX compilation.
Definition Logger.h:58
Intermediate representation wrapper for supported value types in AX as immutable instances.
Definition Value.h:63
Value GetArrayElement(llvm::IRBuilder<> &B, Value idx) const
Emit IR to return a scalar at the provided index from this array value. Will generate invalid IR if t...
Definition Value.h:356
Value BitNot(llvm::IRBuilder<> &B, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to create a unary not instruction on this integer or integer array (i.e. ~value)....
bool IsPtr() const
Return true if this Value is a pointer type.
Definition Value.h:296
Value Or(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a || operation on two scalars. Assumes both inputs are scalars (this checking is c...
Definition Value.h:565
size_t GetArrayNumElements() const
Return the number of elements in this array type.
Definition Value.h:309
Value BitAnd(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a & operation. Both values must be integers.
Definition Value.h:643
Value LessThanEquals(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a <= operation on two values.
Definition Value.h:623
static Value Alloc(llvm::IRBuilder<> &B, llvm::Type *type, llvm::Value *size=nullptr)
Emit IR inserting an allocation at the front of the BasicBlock pointed to by the provided IRBuilder....
Definition Value.h:124
Value ScalarToArray(llvm::IRBuilder<> &B, size_t size) const
Emit IR to broadcast this scalar to a new array. Will generated invalid IR if this is not a scalar or...
Definition Value.h:417
static Value Create(llvm::LLVMContext &C, const ValueType &value)
Create an arithmetic literal.
Definition Value.h:92
llvm::Type * GetUnderlyingScalarType() const
Access the underlying scalar type. This method assumes the current value is a scalar or array.
Definition Value.h:680
Value Assign(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to assign the provided rhs to this value.
Value ScalarToIdentMatrix4(llvm::IRBuilder<> &B) const
Emit IR to create a new 4x4 matrix from this scalar value, adhering to scalar->matrix promotion rules...
Definition Value.h:443
Value BitOr(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a | operation. Both values must be integers.
Definition Value.h:649
Value ShiftLeft(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a << operation. Both values must be integers.
Definition Value.h:631
void ArrayToScalars(llvm::IRBuilder<> &B, std::vector< Value > &elements, const bool load=false) const
Emit IR to extract scalar values from the elements in this array and populate the provided vector wit...
Definition Value.h:392
static Value ScalarsToArray(llvm::IRBuilder<> &B, const std::vector< Value > &values)
Emit IR to create an array from a set of scalar values. Will generate invalid IR if the values are no...
Definition Value.h:158
Value Load(llvm::IRBuilder<> &B) const
Emit IR to load the current value. Not typically required to call directly. Will generate invalid IR ...
Definition Value.h:335
Value IsNan(llvm::IRBuilder<> &B) const
Emit IR to check whether this value is NaN. Only works on Float types and will generate invalid IR if...
Definition Value.h:323
bool IsVoid() const
Return true if this value represents a void type. This is typically only possible for void function r...
Definition Value.h:209
Value ScalarBoolComparison(llvm::IRBuilder<> &B) const
Emit IR to perform standard boolean comparison on this scalar i.e. bool(scalar) or bool(scalar == 0)....
Value Modulo(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a FLOORED % operation on two values.
bool operator!() const
See bool operator.
Definition Value.h:204
Value Increment(llvm::IRBuilder<> &B, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to increment this scalar (i.e. value + 1). Will return Value::Invalid() and report a message ...
Value BitXor(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a ^ operation. Both values must be integers.
Definition Value.h:655
llvm::Value * GetValue() const
Access the underlying llvm Value.
Definition Value.h:673
bool IsMatrix() const
Return true if the underlying type is an matrix 3/4 type.
Definition Value.h:278
Value(const Value &)=default
Value(llvm::Constant *costant)
Initialize from a constant value.
Definition Value.h:77
Value & operator=(const Value &)=default
Value NotEquals(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a != operation on two values.
Value LessThan(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a < operation on two values.
Definition Value.h:617
bool IsVector() const
Return true if the underlying type is an vector 2/3/4 type.
Definition Value.h:269
bool IsString() const
Return true if the underlying type is a string type.
Definition Value.h:289
bool IsInteger() const
Return true if the underlying type is an integer type.
Definition Value.h:240
Value Divide(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a / operation on two values If the denominator is constant and zero,...
Value ScalarToIdentMatrix3(llvm::IRBuilder<> &B) const
Emit IR to create a new 3x3 matrix from this scalar value, adhering to scalar->matrix promotion rules...
Definition Value.h:435
bool IsConstant() const
Return true if this Value is a constant.
Definition Value.h:303
Value ShiftRight(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a >> operation. Both values must be integers.
Definition Value.h:637
static Value Return(llvm::IRBuilder<> &B, Value *ret=nullptr)
Create a value that represents a return value from a function. Really only intended to be used by the...
Definition Value.h:101
Value Not(llvm::IRBuilder<> &B, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to create a unary not instruction on this scalar or integer array (i.e. !value)....
Value Negate(llvm::IRBuilder<> &B, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to create a unary not instruction on this scalar or array (i.e. -value). If the value is not ...
Value One() const
Create a new one scalar Value using the underlying scalar precision of this Value....
Definition Value.h:190
Value & operator=(Value &&)=default
Value Multiply(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a * operation on two values.
Value Binary(llvm::IRBuilder<> &B, Value rhs, const ast::tokens::OperatorToken &op, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a binary operation on this LHS value and a provided RHS value. If the operation is...
Definition Value.h:511
Value LoadIfPtr(llvm::IRBuilder<> &B) const
Emit IR to load the current value if it is a ptr. Not typically required to call directly.
Definition Value.h:346
Value And(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a && operation on two scalars. Assumes both inputs are scalars (this checking is c...
Definition Value.h:548
Value Select(llvm::IRBuilder<> &B, const Value &trueval, const Value &falseval) const
Emit IR to select a value based on this boolean scalar value. Will generate invalid IR if this is not...
Value Equals(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a == operation on two values.
Value GreaterThanEquals(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a >= operation on two values.
Definition Value.h:611
void Print() const
Dump this value/type to llvm::errs.
llvm::Type * GetUnderlyingType() const
Access the underlying llvm Type.
Definition Value.h:676
Value Subtract(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a - operation on two values.
Value TruncatedModulo(llvm::IRBuilder<> &B, Value rhs) const
Emit IR to perform a TRUNCATED % operation on two values.
Value Decrement(llvm::IRBuilder<> &B, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to increment this scalar (i.e. value - 1). Will return Value::Invalid() and report a message ...
Value Zero() const
Create a new zero scalar Value using the underlying scalar precision of this Value....
Definition Value.h:182
static Value Invalid()
Return an invalid Value. This is used to represent various fail cases. Note that the operator bool(Va...
Definition Value.h:88
Value Add(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a + operation on two values.
bool IsArray() const
Return true if the underlying type is an array type.
Definition Value.h:260
bool IsFloat() const
Return true if the underlying type is an floating point type (float or double).
Definition Value.h:251
bool IsBool() const
Return true if the underlying type is a bool type.
Definition Value.h:219
Value CastToPrecision(llvm::IRBuilder<> &B, llvm::Type *precision) const
Emit IR to cast this scalar or array to a new value of the provided scalar precision....
Value(llvm::Value *val, llvm::Type *utype)
Create a Value with a provided underlying type.
Definition Value.h:71
Value GreaterThan(llvm::IRBuilder<> &B, Value rhs, Logger *log=nullptr, const ax::ast::Node *node=nullptr) const
Emit IR to perform a > operation on two values.
Definition Value.h:605
Value GetArrayElement(llvm::IRBuilder<> &B, uint64_t idx) const
Emit IR to return a scalar at the provided index from this array value. Will generate invalid IR if t...
Definition Value.h:374
bool IsScalar() const
Return true if the underlying type is a scalar type (bool, int or float).
Definition Value.h:230
static bool Supports(llvm::Type *utype)
Return true if the underlying type held by utype is supported via the interface of this Value class.
Definition Value.h:109
Definition FunctionRegistry.h:23
OperatorToken
Definition Tokens.h:151
@ BITOR
Definition Tokens.h:181
@ DIVIDE
Definition Tokens.h:158
@ LESSTHANOREQUAL
Definition Tokens.h:174
@ SHIFTRIGHT
Definition Tokens.h:179
@ MORETHANOREQUAL
Definition Tokens.h:173
@ EQUALSEQUALS
RELATIONAL.
Definition Tokens.h:169
@ BITXOR
Definition Tokens.h:182
@ BITAND
Definition Tokens.h:180
@ AND
LOGICAL.
Definition Tokens.h:163
@ PLUS
ARITHMETIC.
Definition Tokens.h:155
@ LESSTHAN
Definition Tokens.h:172
@ OR
Definition Tokens.h:164
@ MODULO
Definition Tokens.h:159
@ MORETHAN
Definition Tokens.h:171
@ NOTEQUALS
Definition Tokens.h:170
@ MULTIPLY
Definition Tokens.h:157
@ SHIFTLEFT
BITWISE.
Definition Tokens.h:178
@ MINUS
Definition Tokens.h:156
bool AssertOpaquePtrs(llvm::Value *opaque, llvm::Type *type)
Definition Utils.h:43
llvm::Constant * llvmConstant(const T t, llvm::Type *type)
Returns an llvm Constant holding a scalar value.
Definition Types.h:360
Definition Exceptions.h:13
Utility code generation methods for performing various llvm operations.
The base abstract node which determines the interface and required methods for all derived concrete n...
Definition AST.h:103
static llvm::Type * get(llvm::LLVMContext &C)
Return an LLVM type which represents T.
Definition Types.h:81
#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