OpenVDB  12.1.0
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 
27 namespace openvdb {
29 namespace OPENVDB_VERSION_NAME {
30 
31 namespace ax {
32 namespace 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 {
64 public:
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  {
126  OPENVDB_ASSERT(Value::Supports(type));
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  {
184  return Value(llvmConstant<uint64_t>(0, this->GetUnderlyingScalarType()));
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  {
192  return Value(llvmConstant<uint64_t>(1, this->GetUnderlyingScalarType()));
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());
360  OPENVDB_ASSERT(idx.IsInteger());
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());
553  OPENVDB_ASSERT(rhs.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());
570  OPENVDB_ASSERT(rhs.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 
687 private:
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,
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());
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 
787 private:
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 
Definition: axparser.h:140
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
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(llvm::Value *val, llvm::Type *utype)
Create a Value with a provided underlying type.
Definition: Value.h:71
Definition: FunctionRegistry.h:23
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
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
Definition: axparser.h:141
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
bool IsArray() const
Return true if the underlying type is an array type.
Definition: Value.h:260
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
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
Definition: axparser.h:147
std::string typeStringFromToken(const CoreType type)
Definition: Tokens.h:118
Definition: axparser.h:144
Value One() const
Create a new one scalar Value using the underlying scalar precision of this Value. Does not generate IR, however will return an invalid constant if this Value is a string.
Definition: Value.h:190
#define OPENVDB_ASSERT_MESSAGE(X, MSG)
Definition: Assert.h:42
bool IsInteger() const
Return true if the underlying type is an integer type.
Definition: Value.h:240
#define OPENVDB_AX_API
Definition: Platform.h:312
Definition: axparser.h:136
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
ArrayPacks represent temporary container creations of arbitrary sizes, typically generated through th...
Definition: AST.h:1785
Definition: axparser.h:145
LLVM type mapping from pod types.
Definition: Types.h:67
Consolidated llvm types for most supported types.
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
OutGridT XformOp & op
Definition: ValueTransformer.h:139
Definition: axparser.h:143
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
bool error(const std::string &message, const CodeLocation &lineCol=CodeLocation(0, 0))
Log a compiler error and its offending code location. If the offending location is (0...
llvm::Type * GetUnderlyingScalarType() const
Access the underlying scalar type. This method assumes the current value is a scalar or array...
Definition: Value.h:680
size_t GetArrayNumElements() const
Return the number of elements in this array type.
Definition: Value.h:309
bool IsFloat() const
Return true if the underlying type is an floating point type (float or double).
Definition: Value.h:251
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
bool IsBool() const
Return true if the underlying type is a bool type.
Definition: Value.h:219
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
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
virtual const Node * child(const size_t index) const =0
Virtual method for accessing child information. Returns a const pointer to a child node at the given ...
Value ScalarBoolComparison(llvm::IRBuilder<> &B) const
Emit IR to perform standard boolean comparison on this scalar i.e. bool(scalar) or bool(scalar == 0)...
CoreType
Definition: Tokens.h:31
Definition: axparser.h:151
Definition: axparser.h:137
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
Definition: axparser.h:146
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
bool operator!() const
See bool operator.
Definition: Value.h:204
static Value Create(llvm::LLVMContext &C, const ValueType &value)
Create an arithmetic literal.
Definition: Value.h:92
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
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
Value Zero() const
Create a new zero scalar Value using the underlying scalar precision of this Value. Does not generate IR, however will return an invalid constant if this Value is a string.
Definition: Value.h:182
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
Definition: Exceptions.h:13
OutGridT const XformOp bool bool
Definition: ValueTransformer.h:609
bool IsVector() const
Return true if the underlying type is an vector 2/3/4 type.
Definition: Value.h:269
Definition: axparser.h:135
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
Definition: axparser.h:150
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
std::string operatorNameFromToken(const OperatorToken token)
Definition: Tokens.h:257
Definition: axparser.h:142
Definition: axparser.h:149
llvm::Type * GetUnderlyingType() const
Access the underlying llvm Type.
Definition: Value.h:676
bool IsScalar() const
Return true if the underlying type is a scalar type (bool, int or float).
Definition: Value.h:230
Definition: axparser.h:148
Definition: axparser.h:139
bool warning(const std::string &message, const CodeLocation &lineCol=CodeLocation(0, 0))
Log a compiler warning and its offending code location. If the offending location is (0...
Logger for collecting errors and warnings that occur during AX compilation.
Definition: Logger.h:57
bool AssertOpaquePtrs([[maybe_unused]]llvm::Value *opaque, [[maybe_unused]]llvm::Type *type)
Definition: Utils.h:43
Definition: axparser.h:152
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
Definition: axparser.h:138
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 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
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
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
bool IsConstant() const
Return true if this Value is a constant.
Definition: Value.h:303
Intermediate representation wrapper for supported value types in AX as immutable instances.
Definition: Value.h:62
Utility code generation methods for performing various llvm operations.
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
OperatorToken
Definition: Tokens.h:150
The base abstract node which determines the interface and required methods for all derived concrete n...
Definition: AST.h:102
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
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
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
bool IsPtr() const
Return true if this Value is a pointer type.
Definition: Value.h:296
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
bool IsString() const
Return true if the underlying type is a string type.
Definition: Value.h:289
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
Value(llvm::Constant *costant)
Initialize from a constant value.
Definition: Value.h:77