Proteus
Programmable JIT compilation and optimization for C/C++ using LLVM
Loading...
Searching...
No Matches
Var.h
Go to the documentation of this file.
1#ifndef PROTEUS_FRONTEND_VAR_H
2#define PROTEUS_FRONTEND_VAR_H
3
4#include "proteus/Error.h"
8
9#include <string>
10#include <type_traits>
11
12namespace proteus {
13
14// Primary template declaration
15template <typename T, typename = void> struct Var;
16
17// Specialization for arithmetic types (including references to arithmetic
18// types).
19template <typename T>
20struct Var<T, std::enable_if_t<is_scalar_arithmetic_v<T>>> {
25 unsigned AddrSpace = 0;
26
27 using ValueType = T;
28 using ElemType = T;
29
31 : CB(CBIn), Slot(A.Slot), ValueTy(A.ValueTy), AllocTy(A.AllocTy),
32 AddrSpace(A.AddrSpace) {}
33
34 // Conversion constructor from Var<U> where U can convert to T.
35 template <typename U,
36 typename = std::enable_if_t<std::is_convertible_v<U, T> &&
37 (!std::is_same_v<U, T>)>>
38 Var(const Var<U> &V);
39
40 // Copy constructor: aliases the same alloca slot. This is effectively a
41 // "shallow copy" that creates another Var handle to the same storage.
42 Var(const Var &V) = default;
43 Var(Var &&) = default;
44
45 Var &operator=(Var &&V);
46
47 // Assignment operators
48 Var &operator=(const Var &V);
49
50 template <typename U> Var &operator=(const Var<U> &V);
51
52 template <typename U> Var &operator=(const U &ConstValue);
53
54 Var<std::add_pointer_t<T>> getAddress();
55
59 template <typename U> auto convert() const;
60
61 // Load / store helpers.
62 IRValue *loadValue() const {
63 if constexpr (std::is_reference_v<T>)
64 return CB.loadFromPointee(Slot, AllocTy, ValueTy);
65 else
66 return CB.loadScalar(Slot, ValueTy);
67 }
68 void storeValue(IRValue *Val) {
69 if constexpr (std::is_reference_v<T>)
70 CB.storeToPointee(Slot, AllocTy, Val);
71 else
72 CB.storeScalar(Slot, Val);
73 }
74 IRValue *getSlot() const { return Slot; }
75 IRType getValueType() const { return ValueTy; }
76 IRType getAllocatedType() const { return AllocTy; }
77
78 // Arithmetic operators
79 template <typename U>
81
82 template <typename U>
83 std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
84 operator+(const U &ConstValue) const;
85
86 template <typename U>
88
89 template <typename U>
90 std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
91 operator-(const U &ConstValue) const;
92
93 template <typename U>
95
96 template <typename U>
97 std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
98 operator*(const U &ConstValue) const;
99
100 template <typename U>
101 Var<std::common_type_t<T, U>> operator/(const Var<U> &Other) const;
102
103 template <typename U>
104 std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
105 operator/(const U &ConstValue) const;
106
107 template <typename U>
108 Var<std::common_type_t<T, U>> operator%(const Var<U> &Other) const;
109
110 template <typename U>
111 std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
112 operator%(const U &ConstValue) const;
113
114 // Unary operators
116 Var<bool> operator!() const;
117
118 // Compound assignment operators
119 template <typename U> Var &operator+=(const Var<U> &Other);
120
121 template <typename U> Var &operator+=(const U &ConstValue);
122
123 template <typename U> Var &operator-=(const Var<U> &Other);
124
125 template <typename U> Var &operator-=(const U &ConstValue);
126
127 template <typename U> Var &operator*=(const Var<U> &Other);
128
129 template <typename U> Var &operator*=(const U &ConstValue);
130
131 template <typename U> Var &operator/=(const Var<U> &Other);
132
133 template <typename U> Var &operator/=(const U &ConstValue);
134
135 template <typename U> Var &operator%=(const Var<U> &Other);
136
137 template <typename U> Var &operator%=(const U &ConstValue);
138
139 // Comparison operators
140 template <typename U>
141 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
142 operator>(const Var<U> &Other) const;
143
144 template <typename U>
145 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
146 operator>=(const Var<U> &Other) const;
147
148 template <typename U>
149 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
150 operator<(const Var<U> &Other) const;
151
152 template <typename U>
153 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
154 operator<=(const Var<U> &Other) const;
155
156 template <typename U>
157 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
158 operator==(const Var<U> &Other) const;
159
160 template <typename U>
161 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
162 operator!=(const Var<U> &Other) const;
163
164 template <typename U>
165 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
166 operator>(const U &ConstValue) const;
167
168 template <typename U>
169 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
170 operator>=(const U &ConstValue) const;
171
172 template <typename U>
173 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
174 operator<(const U &ConstValue) const;
175
176 template <typename U>
177 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
178 operator<=(const U &ConstValue) const;
179
180 template <typename U>
181 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
182 operator==(const U &ConstValue) const;
183
184 template <typename U>
185 std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
186 operator!=(const U &ConstValue) const;
187};
188
189// Specialization for array types
190template <typename T> struct Var<T, std::enable_if_t<std::is_array_v<T>>> {
195 unsigned AddrSpace = 0;
196
197 using ValueType = T;
198 using ElemType = std::remove_extent_t<T>;
199
201 : CB(CBIn), Slot(A.Slot), ValueTy(A.ValueTy), AllocTy(A.AllocTy),
202 AddrSpace(A.AddrSpace) {}
203
204 IRValue *getSlot() const { return Slot; }
205 IRType getValueType() const { return ValueTy; }
206 IRType getAllocatedType() const { return AllocTy; }
207
208 // Load / store: loading an entire array is not supported.
210 reportFatalError("Cannot load entire array as a value");
211 return nullptr;
212 }
214 reportFatalError("Cannot store value to entire array");
215 }
216
217 Var<std::add_lvalue_reference_t<ElemType>> operator[](size_t Index);
218
219 template <typename IdxT>
220 std::enable_if_t<std::is_integral_v<IdxT>,
222 operator[](const Var<IdxT> &Index);
223
225};
226
227// Specialization for pointer types (including references to pointers)
228template <typename T> struct Var<T, std::enable_if_t<is_pointer_unref_v<T>>> {
233 unsigned AddrSpace = 0;
234
235 using ValueType = T;
236 using ElemType = std::remove_pointer_t<std::remove_reference_t<T>>;
237
239 : CB(CBIn), Slot(A.Slot), ValueTy(A.ValueTy), AllocTy(A.AllocTy),
240 AddrSpace(A.AddrSpace) {}
241
242 IRValue *getSlot() const { return Slot; }
243 IRType getValueType() const { return ValueTy; }
244 IRType getAllocatedType() const { return AllocTy; }
245
246 // Load / store the pointer value itself from/to the pointer slot.
247 IRValue *loadAddress() const { return CB.loadAddress(Slot, AllocTy); }
248 void storeAddress(IRValue *Ptr) { CB.storeAddress(Slot, Ptr); }
249
250 // Load / store through the pointer (dereference).
252 return CB.loadFromPointee(Slot, AllocTy, ValueTy);
253 }
254 void storeValue(IRValue *Val) { CB.storeToPointee(Slot, AllocTy, Val); }
255
256 Var<std::add_lvalue_reference_t<ElemType>> operator[](size_t Index);
257
258 template <typename IdxT>
259 std::enable_if_t<std::is_arithmetic_v<IdxT>,
261 operator[](const Var<IdxT> &Index);
262
264
266
267 template <typename OffsetT>
268 std::enable_if_t<std::is_arithmetic_v<OffsetT>,
270 operator+(const Var<OffsetT> &Offset) const;
271
272 template <typename OffsetT>
273 std::enable_if_t<std::is_arithmetic_v<OffsetT>,
275 operator+(OffsetT Offset) const;
276
277 template <typename OffsetT>
278 friend std::enable_if_t<std::is_arithmetic_v<OffsetT>,
280 operator+(OffsetT Offset, const Var &Ptr) {
281 return Ptr + Offset;
282 }
283};
284
285// Non-member arithmetic operators for Var
286template <typename T, typename U>
287std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
288 Var<std::common_type_t<T, U>>>
289operator+(const T &ConstValue, const Var<U> &Var);
290
291template <typename T, typename U>
292std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
293 Var<std::common_type_t<T, U>>>
294operator-(const T &ConstValue, const Var<U> &V);
295
296template <typename T, typename U>
297std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
298 Var<std::common_type_t<T, U>>>
299operator*(const T &ConstValue, const Var<U> &V);
300
301template <typename T, typename U>
302std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
303 Var<std::common_type_t<T, U>>>
304operator/(const T &ConstValue, const Var<U> &V);
305
306template <typename T, typename U>
307std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
308 Var<std::common_type_t<T, U>>>
309operator%(const T &ConstValue, const Var<U> &V);
310
311// ---------------------------------------------------------------------------
312// Free helper functions (type conversion)
313// ---------------------------------------------------------------------------
314
315// Value-level type conversion — internal implementation detail.
316// Use Var::convert<U>() for user-facing type conversions.
317namespace detail {
318template <typename FromT, typename ToT>
320 using From = remove_cvref_t<FromT>;
321 using To = remove_cvref_t<ToT>;
322 static_assert(std::is_arithmetic_v<From>, "From type must be arithmetic");
323 static_assert(std::is_arithmetic_v<To>, "To type must be arithmetic");
324
325 if constexpr (std::is_same_v<From, To>)
326 return V;
328}
329} // namespace detail
330
331// Allocate a new Var of type T using CB.
332template <typename T>
333Var<T> declVar(CodeBuilder &CB, const std::string &Name = "var") {
334 static_assert(!std::is_array_v<T>, "Expected non-array type");
335 static_assert(!std::is_reference_v<T>,
336 "declVar does not support reference types");
337
338 if constexpr (std::is_pointer_v<T>) {
340 return Var<T>{CB.allocPointer(Name, ElemIRTy), CB};
341 } else {
342 IRType AllocaIRTy = TypeMap<T>::get();
343 return Var<T>{CB.allocScalar(Name, AllocaIRTy), CB};
344 }
345}
346
347// Allocate and initialize a Var of type T.
348template <typename T>
349Var<T> defVar(CodeBuilder &CB, const T &Val, const std::string &Name = "var") {
350 using RawT = std::remove_const_t<T>;
351 Var<RawT> V = declVar<RawT>(CB, Name);
352 V = Val;
353 return Var<T>(V);
354}
355
356// ---------------------------------------------------------------------------
357// Operator implementation helpers
358// ---------------------------------------------------------------------------
359
360template <typename T, typename U>
361Var<std::common_type_t<remove_cvref_t<T>, remove_cvref_t<U>>>
362binOp(const Var<T> &L, const Var<U> &R, ArithOp Op) {
363 using CommonT = std::common_type_t<remove_cvref_t<T>, remove_cvref_t<U>>;
364
365 CodeBuilder &CB = L.CB;
366 if (&CB != &R.CB)
367 reportFatalError("Variables should belong to the same function");
368
369 IRValue *LHS = detail::convert<T, CommonT>(CB, L.loadValue());
370 IRValue *RHS = detail::convert<U, CommonT>(CB, R.loadValue());
371
372 IRValue *Result = CB.createArith(Op, LHS, RHS, TypeMap<CommonT>::get());
373
374 auto ResultVar = declVar<CommonT>(CB, "res.");
375 ResultVar.storeValue(Result);
376
377 return ResultVar;
378}
379
380template <typename T, typename U>
381Var<T, std::enable_if_t<is_scalar_arithmetic_v<T>>> &
382compoundAssignConst(Var<T, std::enable_if_t<is_scalar_arithmetic_v<T>>> &LHS,
383 const U &ConstValue, ArithOp Op) {
384 static_assert(std::is_convertible_v<remove_cvref_t<U>, remove_cvref_t<T>>,
385 "U must be convertible to T");
386
387 IRType RHSType = TypeMap<remove_cvref_t<U>>::get();
388
389 IRValue *RHS = nullptr;
390 if constexpr (std::is_integral_v<remove_cvref_t<U>>) {
391 RHS = LHS.CB.getConstantInt(RHSType, ConstValue);
392 } else {
393 RHS = LHS.CB.getConstantFP(RHSType, ConstValue);
394 }
395
396 IRValue *LHSVal = LHS.loadValue();
397
398 RHS = detail::convert<U, T>(LHS.CB, RHS);
399 IRValue *Result =
400 LHS.CB.createArith(Op, LHSVal, RHS, TypeMap<remove_cvref_t<T>>::get());
401
402 LHS.storeValue(Result);
403 return LHS;
404}
405
406template <typename T, typename U>
407Var<bool> cmpOp(const Var<T> &L, const Var<U> &R, CmpOp Op) {
408 CodeBuilder &CB = L.CB;
409 if (&CB != &R.CB)
410 reportFatalError("Variables should belong to the same function");
411
412 IRValue *LHS = L.loadValue();
413 IRValue *RHS = detail::convert<U, T>(CB, R.loadValue());
414
415 IRValue *Result =
416 CB.createCmp(Op, LHS, RHS, TypeMap<remove_cvref_t<T>>::get());
417
418 auto ResultVar = declVar<bool>(CB, "res.");
419 ResultVar.storeValue(Result);
420
421 return ResultVar;
422}
423
424// ---------------------------------------------------------------------------
425// Var member operator implementations
426// ---------------------------------------------------------------------------
427
428template <typename T>
429template <typename U>
431 using ResultT = std::remove_reference_t<U>;
432 Var<ResultT> Res = declVar<ResultT>(this->CB, "convert.");
433 IRValue *Converted = detail::convert<T, U>(this->CB, this->loadValue());
434 Res.storeValue(Converted);
435 return Res;
436}
437
438template <typename T>
439template <typename U, typename>
441 : Var(V.CB.allocScalar("conv.var", TypeMap<remove_cvref_t<T>>::get()),
442 V.CB) {
443 auto Converted = detail::convert<U, T>(CB, V.loadValue());
444 storeValue(Converted);
445}
446
447template <typename T>
450 static_assert(is_mutable_v<T>, "Cannot assign to Var<const T>");
451 storeValue(V.loadValue());
452 return *this;
453}
454
455template <typename T>
458 static_assert(is_mutable_v<T>, "Cannot assign to Var<const T>");
459 storeValue(V.loadValue());
460 return *this;
461}
462
463template <typename T>
466 if constexpr (std::is_reference_v<T>) {
467 // For a reference Var the slot holds a pointer; load that pointer and
468 // expose it as the address.
469 IRValue *PtrVal = CB.loadAddress(Slot, AllocTy);
470 auto A = CB.allocPointer("addr.ref.tmp", ValueTy, AddrSpace);
471 CB.storeAddress(A.Slot, PtrVal);
472 return Var<std::add_pointer_t<T>>(A, CB);
473 }
474
475 auto A = CB.allocPointer("addr.tmp", AllocTy, AddrSpace);
476 CB.storeAddress(A.Slot, Slot);
477 return Var<std::add_pointer_t<T>>(A, CB);
478}
479
480template <typename T>
481template <typename U>
484 const Var<U> &V) {
485 static_assert(is_mutable_v<T>, "Cannot assign to Var<const T>");
486 auto Converted = detail::convert<U, T>(CB, V.loadValue());
487 storeValue(Converted);
488 return *this;
489}
490
491template <typename T>
492template <typename U>
495 const U &ConstValue) {
496 static_assert(is_mutable_v<T>, "Cannot assign to Var<const T>");
497 static_assert(std::is_arithmetic_v<U>,
498 "Can only assign arithmetic types to Var");
499
500 IRType LHSType = getValueType();
501
502 if (isIntegerKind(LHSType)) {
503 storeValue(CB.getConstantInt(LHSType, ConstValue));
504 } else if (isFloatingPointKind(LHSType)) {
505 storeValue(CB.getConstantFP(LHSType, ConstValue));
506 } else {
507 reportFatalError("Unsupported type");
508 }
509
510 return *this;
511}
512
513template <typename T>
514template <typename U>
517 const Var<U> &Other) const {
518 return binOp(*this, Other, ArithOp::Add);
519}
520
521template <typename T>
522template <typename U>
525 const Var<U> &Other) const {
526 return binOp(*this, Other, ArithOp::Sub);
527}
528
529template <typename T>
530template <typename U>
533 const Var<U> &Other) const {
534 return binOp(*this, Other, ArithOp::Mul);
535}
536
537template <typename T>
538template <typename U>
541 const Var<U> &Other) const {
542 return binOp(*this, Other, ArithOp::Div);
543}
544
545template <typename T>
546template <typename U>
549 const Var<U> &Other) const {
550 return binOp(*this, Other, ArithOp::Rem);
551}
552
553// Arithmetic operators with ConstValue
554template <typename T>
555template <typename U>
556std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
558 const U &ConstValue) const {
559 static_assert(std::is_arithmetic_v<U>,
560 "Can only add arithmetic types to Var");
561 Var<U> Tmp = defVar<U>(CB, ConstValue, "tmp.");
562 return (*this) + Tmp;
563}
564
565template <typename T>
566template <typename U>
567std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
569 const U &ConstValue) const {
570 static_assert(std::is_arithmetic_v<U>,
571 "Can only subtract arithmetic types from Var");
572 Var<U> Tmp = defVar<U>(CB, ConstValue, "tmp.");
573 return (*this) - Tmp;
574}
575
576template <typename T>
577template <typename U>
578std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
580 const U &ConstValue) const {
581 static_assert(std::is_arithmetic_v<U>,
582 "Can only multiply Var by arithmetic types");
583 Var<U> Tmp = defVar<U>(CB, ConstValue, "tmp.");
584 return (*this) * Tmp;
585}
586
587template <typename T>
588template <typename U>
589std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
591 const U &ConstValue) const {
592 static_assert(std::is_arithmetic_v<U>,
593 "Can only divide Var by arithmetic types");
594 Var<U> Tmp = defVar<U>(CB, ConstValue, "tmp.");
595 return (*this) / Tmp;
596}
597
598template <typename T>
599template <typename U>
600std::enable_if_t<std::is_arithmetic_v<U>, Var<std::common_type_t<T, U>>>
602 const U &ConstValue) const {
603 static_assert(std::is_arithmetic_v<U>,
604 "Can only modulo Var by arithmetic types");
605 Var<U> Tmp = defVar<U>(CB, ConstValue, "tmp.");
606 return (*this) % Tmp;
607}
608
609// Compound assignment operators for Var
610template <typename T>
611template <typename U>
614 const Var<U> &Other) {
615 static_assert(is_mutable_v<T>, "Cannot use += on Var<const T>");
616 auto Result = (*this) + Other;
617 *this = Result;
618 return *this;
619}
620
621template <typename T>
622template <typename U>
625 const U &ConstValue) {
626 static_assert(is_mutable_v<T>, "Cannot use += on Var<const T>");
627 static_assert(std::is_arithmetic_v<U>,
628 "Can only add arithmetic types to Var");
629 return compoundAssignConst(*this, ConstValue, ArithOp::Add);
630}
631
632template <typename T>
633template <typename U>
636 const Var<U> &Other) {
637 static_assert(is_mutable_v<T>, "Cannot use -= on Var<const T>");
638 auto Result = (*this) - Other;
639 *this = Result;
640 return *this;
641}
642
643template <typename T>
644template <typename U>
647 const U &ConstValue) {
648 static_assert(is_mutable_v<T>, "Cannot use -= on Var<const T>");
649 static_assert(std::is_arithmetic_v<U>,
650 "Can only subtract arithmetic types from Var");
651 return compoundAssignConst(*this, ConstValue, ArithOp::Sub);
652}
653
654template <typename T>
655template <typename U>
658 const Var<U> &Other) {
659 static_assert(is_mutable_v<T>, "Cannot use *= on Var<const T>");
660 auto Result = (*this) * Other;
661 *this = Result;
662 return *this;
663}
664
665template <typename T>
666template <typename U>
669 const U &ConstValue) {
670 static_assert(is_mutable_v<T>, "Cannot use *= on Var<const T>");
671 static_assert(std::is_arithmetic_v<U>,
672 "Can only multiply Var by arithmetic types");
673 return compoundAssignConst(*this, ConstValue, ArithOp::Mul);
674}
675
676template <typename T>
677template <typename U>
680 const Var<U> &Other) {
681 static_assert(is_mutable_v<T>, "Cannot use /= on Var<const T>");
682 auto Result = (*this) / Other;
683 *this = Result;
684 return *this;
685}
686
687template <typename T>
688template <typename U>
691 const U &ConstValue) {
692 static_assert(is_mutable_v<T>, "Cannot use /= on Var<const T>");
693 static_assert(std::is_arithmetic_v<U>,
694 "Can only divide Var by arithmetic types");
695 return compoundAssignConst(*this, ConstValue, ArithOp::Div);
696}
697
698template <typename T>
699template <typename U>
702 const Var<U> &Other) {
703 static_assert(is_mutable_v<T>, "Cannot use %= on Var<const T>");
704 auto Result = (*this) % Other;
705 *this = Result;
706 return *this;
707}
708
709template <typename T>
710template <typename U>
713 const U &ConstValue) {
714 static_assert(is_mutable_v<T>, "Cannot use %= on Var<const T>");
715 static_assert(std::is_arithmetic_v<U>,
716 "Can only modulo Var by arithmetic types");
717 return compoundAssignConst(*this, ConstValue, ArithOp::Rem);
718}
719
720template <typename T>
723 auto MinusOne = defVar<remove_cvref_t<T>>(
724 CB, static_cast<remove_cvref_t<T>>(-1), "minus_one.");
725 return MinusOne * (*this);
726}
727
728template <typename T>
731 IRValue *V = loadValue();
732 IRValue *ResV = nullptr;
733 if constexpr (std::is_same_v<remove_cvref_t<T>, bool>) {
734 ResV = CB.createNot(V);
735 } else if constexpr (std::is_integral_v<remove_cvref_t<T>>) {
736 IRValue *Zero = CB.getConstantInt(getValueType(), 0);
737 ResV = CB.createCmp(CmpOp::EQ, V, Zero, getValueType());
738 } else {
739 IRValue *Zero = CB.getConstantFP(getValueType(), 0.0);
740 ResV = CB.createCmp(CmpOp::EQ, V, Zero, getValueType());
741 }
742 auto Ret = declVar<bool>(CB, "not.");
743 Ret.storeValue(ResV);
744 return Ret;
745}
746
747template <typename T>
750 auto A = CB.getElementPtr(Slot, AllocTy, Index, ValueTy);
752}
753
754template <typename T>
755template <typename IdxT>
756std::enable_if_t<std::is_integral_v<IdxT>,
759 const Var<IdxT> &Index) {
760 auto A = CB.getElementPtr(Slot, AllocTy, Index.loadValue(), ValueTy);
762}
763
764template <typename T>
765Var<std::add_lvalue_reference_t<
766 std::remove_pointer_t<std::remove_reference_t<T>>>>
768 using ElemT = std::remove_pointer_t<std::remove_reference_t<T>>;
769 IRType ElemIRTy = TypeMap<ElemT>::get();
770 IRValue *Ptr = CB.loadAddress(Slot, AllocTy);
771 auto A = CB.getElementPtr(Ptr, AllocTy, Index, ElemIRTy);
772 return Var<std::add_lvalue_reference_t<
773 std::remove_pointer_t<std::remove_reference_t<T>>>>(A, CB);
774}
775
776template <typename T>
777template <typename IdxT>
778std::enable_if_t<std::is_arithmetic_v<IdxT>,
779 Var<std::add_lvalue_reference_t<
780 std::remove_pointer_t<std::remove_reference_t<T>>>>>
782 const Var<IdxT> &Index) {
783 using ElemT = std::remove_pointer_t<std::remove_reference_t<T>>;
784 IRType ElemIRTy = TypeMap<ElemT>::get();
785 IRValue *Ptr = CB.loadAddress(Slot, AllocTy);
786 auto A = CB.getElementPtr(Ptr, AllocTy, Index.loadValue(), ElemIRTy);
787 return Var<std::add_lvalue_reference_t<
788 std::remove_pointer_t<std::remove_reference_t<T>>>>(A, CB);
789}
790
791template <typename T>
792Var<std::add_lvalue_reference_t<
793 std::remove_pointer_t<std::remove_reference_t<T>>>>
795 return (*this)[0];
796}
797
798template <typename T>
801 IRValue *PtrVal = CB.loadAddress(Slot, AllocTy);
802 IRType PointeePtrIRTy{IRTypeKind::Pointer, ValueTy.Signed, 0, ValueTy.Kind};
803
804 auto A = CB.allocPointer("addr.ptr.tmp", PointeePtrIRTy, 0);
805 CB.storeAddress(A.Slot, PtrVal);
806 return Var<std::add_pointer_t<T>>(A, CB);
807}
808
809template <typename T>
810template <typename OffsetT>
811std::enable_if_t<std::is_arithmetic_v<OffsetT>,
814 const Var<OffsetT> &Offset) const {
815 IRValue *IdxVal = detail::convert<OffsetT, int64_t>(CB, Offset.loadValue());
816 IRValue *BasePtr = CB.loadAddress(Slot, AllocTy);
817 auto A = CB.getElementPtr(BasePtr, AllocTy, IdxVal, ValueTy);
819}
820
821template <typename T>
822template <typename OffsetT>
823std::enable_if_t<std::is_arithmetic_v<OffsetT>,
826 OffsetT Offset) const {
827 IRValue *IdxVal = CB.getConstantInt(IRType{IRTypeKind::Int64},
828 static_cast<uint64_t>(Offset));
829 IRValue *BasePtr = CB.loadAddress(Slot, AllocTy);
830 auto A = CB.getElementPtr(BasePtr, AllocTy, IdxVal, ValueTy);
832}
833
834// Comparison operators for Var
835template <typename T>
836template <typename U>
837std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
839 const Var<U> &Other) const {
840 return cmpOp(*this, Other, CmpOp::GT);
841}
842
843template <typename T>
844template <typename U>
845std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
847 const Var<U> &Other) const {
848 return cmpOp(*this, Other, CmpOp::GE);
849}
850
851template <typename T>
852template <typename U>
853std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
855 const Var<U> &Other) const {
856 return cmpOp(*this, Other, CmpOp::LT);
857}
858
859template <typename T>
860template <typename U>
861std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
862Var<T, std::enable_if_t<is_scalar_arithmetic_v<T>>>::operator<=(
863 const Var<U> &Other) const {
864 return cmpOp(*this, Other, CmpOp::LE);
865}
866
867template <typename T>
868template <typename U>
869std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
871 const Var<U> &Other) const {
872 return cmpOp(*this, Other, CmpOp::EQ);
873}
874
875template <typename T>
876template <typename U>
877std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
879 const Var<U> &Other) const {
880 return cmpOp(*this, Other, CmpOp::NE);
881}
882
883template <typename T>
884template <typename U>
885std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
887 const U &ConstValue) const {
888 Var<U> Tmp = defVar<U>(CB, ConstValue, "cmp.");
889 return (*this) > Tmp;
890}
891
892template <typename T>
893template <typename U>
894std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
896 const U &ConstValue) const {
897 Var<U> Tmp = defVar<U>(CB, ConstValue, "cmp.");
898 return (*this) >= Tmp;
899}
900
901template <typename T>
902template <typename U>
903std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
905 const U &ConstValue) const {
906 Var<U> Tmp = defVar<U>(CB, ConstValue, "cmp.");
907 return (*this) < Tmp;
908}
909
910template <typename T>
911template <typename U>
912std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
913Var<T, std::enable_if_t<is_scalar_arithmetic_v<T>>>::operator<=(
914 const U &ConstValue) const {
915 auto Tmp = defVar<U>(CB, ConstValue, "cmp.");
916 return (*this) <= Tmp;
917}
918
919template <typename T>
920template <typename U>
921std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
923 const U &ConstValue) const {
924 Var<U> Tmp = defVar<U>(CB, ConstValue, "cmp.");
925 return (*this) == Tmp;
926}
927
928template <typename T>
929template <typename U>
930std::enable_if_t<is_arithmetic_unref_v<U>, Var<bool>>
932 const U &ConstValue) const {
933 auto Tmp = defVar<U>(CB, ConstValue, "cmp.");
934 return (*this) != Tmp;
935}
936
937// Non-member arithmetic operators for Var
938template <typename T, typename U>
939std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
941operator+(const T &ConstValue, const Var<U> &V) {
942 Var<T> Tmp = defVar<T>(V.CB, ConstValue, "tmp.");
943 return Tmp + V;
944}
945
946template <typename T, typename U>
947std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
948 Var<std::common_type_t<T, U>>>
949operator-(const T &ConstValue, const Var<U> &V) {
950 using CommonType = std::common_type_t<T, U>;
951 Var<CommonType> Tmp = defVar<CommonType>(V.CB, ConstValue, "tmp.");
952 return Tmp - V;
953}
954
955template <typename T, typename U>
956std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
957 Var<std::common_type_t<T, U>>>
958operator*(const T &ConstValue, const Var<U> &V) {
959 Var<T> Tmp = defVar<T>(V.CB, ConstValue, "tmp.");
960 return Tmp * V;
961}
962
963template <typename T, typename U>
964std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
965 Var<std::common_type_t<T, U>>>
966operator/(const T &ConstValue, const Var<U> &V) {
967 Var<T> Tmp = defVar<T>(V.CB, ConstValue, "tmp.");
968 return Tmp / V;
969}
970
971template <typename T, typename U>
972std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U>,
973 Var<std::common_type_t<T, U>>>
974operator%(const T &ConstValue, const Var<U> &V) {
975 Var<T> Tmp = defVar<T>(V.CB, ConstValue, "tmp.");
976 return Tmp % V;
977}
978
979// ---------------------------------------------------------------------------
980// Intrinsic emission helpers
981// ---------------------------------------------------------------------------
982
983// Helper struct to convert Var operands to a target type T.
984template <typename T> struct IntrinsicOperandConverter {
986
987 template <typename U> IRValue *operator()(const Var<U> &Operand) const {
988 return detail::convert<U, T>(CB, Operand.loadValue());
989 }
990};
991
992template <typename T, typename... Operands>
993static Var<T> emitIntrinsic(const std::string &IntrinsicName,
994 const Operands &...Ops) {
995 static_assert(sizeof...(Ops) > 0, "Intrinsic requires at least one operand");
996
997 CodeBuilder &CB = std::get<0>(std::tie(Ops...)).CB;
998 auto CheckFn = [&CB](const auto &Operand) {
999 if (&Operand.CB != &CB)
1000 reportFatalError("Variables should belong to the same function");
1001 };
1002 (CheckFn(Ops), ...);
1003
1004 IntrinsicOperandConverter<T> ConvertOperand{CB};
1005
1006 IRType ResultIRTy = TypeMap<T>::get();
1007 IRValue *Call =
1008 CB.emitIntrinsic(IntrinsicName, ResultIRTy, {ConvertOperand(Ops)...});
1009
1010 auto ResultVar = declVar<T>(CB, "res.");
1011 ResultVar.storeValue(Call);
1012 return ResultVar;
1013}
1014
1015// ---------------------------------------------------------------------------
1016// Math intrinsics for Var
1017// ---------------------------------------------------------------------------
1018
1019template <typename T> Var<float> powf(const Var<float> &L, const Var<T> &R) {
1020 static_assert(std::is_convertible_v<T, float>,
1021 "powf requires floating-point type");
1022
1023 auto RFloat = R.template convert<float>();
1024
1025 return emitIntrinsic<float>("powf", L, RFloat);
1026}
1027
1028template <typename T> Var<float> sqrtf(const Var<T> &R) {
1029 static_assert(std::is_convertible_v<T, float>,
1030 "sqrtf requires floating-point type");
1031
1032 auto RFloat = R.template convert<float>();
1033 return emitIntrinsic<float>("sqrtf", RFloat);
1034}
1035
1036template <typename T> Var<float> expf(const Var<T> &R) {
1037 static_assert(std::is_convertible_v<T, float>,
1038 "expf requires floating-point type");
1039
1040 auto RFloat = R.template convert<float>();
1041 return emitIntrinsic<float>("expf", RFloat);
1042}
1043
1044template <typename T> Var<float> sinf(const Var<T> &R) {
1045 static_assert(std::is_convertible_v<T, float>,
1046 "sinf requires floating-point type");
1047
1048 auto RFloat = R.template convert<float>();
1049 return emitIntrinsic<float>("sinf", RFloat);
1050}
1051
1052template <typename T> Var<float> cosf(const Var<T> &R) {
1053 static_assert(std::is_convertible_v<T, float>,
1054 "cosf requires floating-point type");
1055
1056 auto RFloat = R.template convert<float>();
1057 return emitIntrinsic<float>("cosf", RFloat);
1058}
1059
1060template <typename T> Var<float> fabs(const Var<T> &R) {
1061 static_assert(std::is_convertible_v<T, float>,
1062 "fabs requires floating-point type");
1063
1064 auto RFloat = R.template convert<float>();
1065 return emitIntrinsic<float>("fabsf", RFloat);
1066}
1067
1068template <typename T> Var<float> truncf(const Var<T> &R) {
1069 static_assert(std::is_convertible_v<T, float>,
1070 "truncf requires floating-point type");
1071
1072 auto RFloat = R.template convert<float>();
1073 return emitIntrinsic<float>("truncf", RFloat);
1074}
1075
1076template <typename T> Var<float> logf(const Var<T> &R) {
1077 static_assert(std::is_convertible_v<T, float>,
1078 "logf requires floating-point type");
1079
1080 auto RFloat = R.template convert<float>();
1081 return emitIntrinsic<float>("logf", RFloat);
1082}
1083
1084template <typename T> Var<float> absf(const Var<T> &R) {
1085 static_assert(std::is_convertible_v<T, float>,
1086 "absf requires floating-point type");
1087
1088 auto RFloat = R.template convert<float>();
1089 return emitIntrinsic<float>("absf", RFloat);
1090}
1091
1092template <typename T>
1093std::enable_if_t<is_arithmetic_unref_v<T>, Var<remove_cvref_t<T>>>
1094min(const Var<T> &L, const Var<T> &R) {
1095 CodeBuilder &CB = L.CB;
1096 if (&CB != &R.CB)
1097 reportFatalError("Variables should belong to the same function");
1098
1099 auto ResultVar = declVar<remove_cvref_t<T>>(CB, "min_res");
1100 ResultVar = R;
1101 auto CondVar = L < R;
1102 CB.beginIf(CondVar.loadValue(), __builtin_FILE(), __builtin_LINE());
1103 {
1104 ResultVar = L;
1105 }
1106 CB.endIf();
1107 return ResultVar;
1108}
1109
1110template <typename T>
1111std::enable_if_t<is_arithmetic_unref_v<T>, Var<remove_cvref_t<T>>>
1112max(const Var<T> &L, const Var<T> &R) {
1113 CodeBuilder &CB = L.CB;
1114 if (&CB != &R.CB)
1115 reportFatalError("Variables should belong to the same function");
1116
1117 auto ResultVar = declVar<remove_cvref_t<T>>(CB, "max_res");
1118 ResultVar = R;
1119 auto CondVar = L > R;
1120 CB.beginIf(CondVar.loadValue(), __builtin_FILE(), __builtin_LINE());
1121 {
1122 ResultVar = L;
1123 }
1124 CB.endIf();
1125 return ResultVar;
1126}
1127
1128} // namespace proteus
1129
1130#endif // PROTEUS_FRONTEND_VAR_H
Definition CodeBuilder.h:70
virtual VarAlloc allocPointer(const std::string &Name, IRType ElemTy, unsigned AddrSpace=0)=0
virtual IRValue * loadFromPointee(IRValue *Slot, IRType AllocTy, IRType ValueTy)=0
Dereference the pointer stored in Slot, then load the pointee.
virtual IRValue * createArith(ArithOp Op, IRValue *LHS, IRValue *RHS, IRType Ty)=0
virtual void storeAddress(IRValue *Slot, IRValue *Addr)=0
Store Addr into Slot (pointer alloca).
virtual VarAlloc allocScalar(const std::string &Name, IRType ValueTy)=0
virtual void beginIf(IRValue *Cond, const char *File, int Line)=0
virtual void storeScalar(IRValue *Slot, IRValue *Val)=0
Store Val directly into Slot (scalar alloca).
virtual IRValue * createCmp(CmpOp Op, IRValue *LHS, IRValue *RHS, IRType Ty)=0
virtual IRValue * loadAddress(IRValue *Slot, IRType AllocTy)=0
Load the pointer stored in Slot (pointer alloca).
virtual IRValue * loadScalar(IRValue *Slot, IRType ValueTy)=0
Load the value stored directly in Slot (scalar alloca).
virtual IRValue * createCast(IRValue *V, IRType FromTy, IRType ToTy)=0
virtual void storeToPointee(IRValue *Slot, IRType AllocTy, IRValue *Val)=0
Dereference the pointer stored in Slot, then store Val to it.
virtual void endIf()=0
Definition IRValue.h:15
IRValue * convert(CodeBuilder &CB, IRValue *V)
Definition Var.h:319
Definition MemoryCache.h:27
std::remove_cv_t< std::remove_reference_t< T > > remove_cvref_t
Definition TypeTraits.h:11
std::enable_if_t< std::is_arithmetic_v< T > &&std::is_arithmetic_v< U >, Var< std::common_type_t< T, U > > > operator-(const T &ConstValue, const Var< U > &V)
Definition Var.h:949
std::enable_if_t< is_arithmetic_unref_v< T >, Var< remove_cvref_t< T > > > min(const Var< T > &L, const Var< T > &R)
Definition Var.h:1094
Var< T, std::enable_if_t< is_scalar_arithmetic_v< T > > > & compoundAssignConst(Var< T, std::enable_if_t< is_scalar_arithmetic_v< T > > > &LHS, const U &ConstValue, ArithOp Op)
Definition Var.h:382
Var< float > sqrtf(const Var< T > &R)
Definition Var.h:1028
ArithOp
Semantic arithmetic operation selector.
Definition CodeBuilder.h:43
std::enable_if_t< std::is_arithmetic_v< T > &&std::is_arithmetic_v< U >, Var< std::common_type_t< T, U > > > operator+(const T &ConstValue, const Var< U > &Var)
Definition Var.h:941
Var< float > fabs(const Var< T > &R)
Definition Var.h:1060
bool isFloatingPointKind(const IRType &T)
Returns true when T is a floating-point kind (Float or Double).
Definition IRType.h:57
std::enable_if_t< std::is_arithmetic_v< T > &&std::is_arithmetic_v< U >, Var< std::common_type_t< T, U > > > operator*(const T &ConstValue, const Var< U > &V)
Definition Var.h:958
Var< bool > cmpOp(const Var< T > &L, const Var< U > &R, CmpOp Op)
Definition Var.h:407
Var< std::common_type_t< remove_cvref_t< T >, remove_cvref_t< U > > > binOp(const Var< T > &L, const Var< U > &R, ArithOp Op)
Definition Var.h:362
CmpOp
Semantic comparison operation selector.
Definition CodeBuilder.h:46
Var< float > sinf(const Var< T > &R)
Definition Var.h:1044
Var< T > declVar(CodeBuilder &CB, const std::string &Name="var")
Definition Var.h:333
Var< float > logf(const Var< T > &R)
Definition Var.h:1076
void reportFatalError(const llvm::Twine &Reason, const char *FILE, unsigned Line)
Definition Error.cpp:14
static int int Offset
Definition JitInterface.h:102
Var< float > cosf(const Var< T > &R)
Definition Var.h:1052
std::enable_if_t< std::is_arithmetic_v< T > &&std::is_arithmetic_v< U >, Var< std::common_type_t< T, U > > > operator/(const T &ConstValue, const Var< U > &V)
Definition Var.h:966
Var< float > expf(const Var< T > &R)
Definition Var.h:1036
Var< float > powf(const Var< float > &L, const Var< T > &R)
Definition Var.h:1019
Var< float > truncf(const Var< T > &R)
Definition Var.h:1068
Var< T > defVar(CodeBuilder &CB, const T &Val, const std::string &Name="var")
Definition Var.h:349
Var< float > absf(const Var< T > &R)
Definition Var.h:1084
std::enable_if_t< is_arithmetic_unref_v< T >, Var< remove_cvref_t< T > > > max(const Var< T > &L, const Var< T > &R)
Definition Var.h:1112
bool isIntegerKind(const IRType &T)
Returns true when T is an integer kind (Int1, Int16, Int32, or Int64).
Definition IRType.h:51
std::enable_if_t< std::is_arithmetic_v< T > &&std::is_arithmetic_v< U >, Var< std::common_type_t< T, U > > > operator%(const T &ConstValue, const Var< U > &V)
Definition Var.h:974
Definition Hashing.h:184
Definition IRType.h:34
bool Signed
Signedness of the type (meaningful for integer kinds and pointer-to-int).
Definition IRType.h:38
CodeBuilder & CB
Definition Var.h:985
IRValue * operator()(const Var< U > &Operand) const
Definition Var.h:987
Definition TypeMap.h:15
Definition CodeBuilder.h:29
IRType ValueTy
Pointee (element) type.
Definition Var.h:231
void storeAddress(IRValue *Ptr)
Definition Var.h:248
Var(VarAlloc A, CodeBuilder &CBIn)
Definition Var.h:238
friend std::enable_if_t< std::is_arithmetic_v< OffsetT >, Var< T, std::enable_if_t< is_pointer_unref_v< T > > > > operator+(OffsetT Offset, const Var &Ptr)
Definition Var.h:280
std::remove_pointer_t< std::remove_reference_t< T > > ElemType
Definition Var.h:236
std::enable_if_t< std::is_arithmetic_v< IdxT >, Var< std::add_lvalue_reference_t< ElemType > > > operator[](const Var< IdxT > &Index)
IRType AllocTy
Type of the pointer alloca.
Definition Var.h:232
Var(VarAlloc A, CodeBuilder &CBIn)
Definition Var.h:30
Var(VarAlloc A, CodeBuilder &CBIn)
Definition Var.h:200
Var< std::add_pointer_t< ValueType > > getAddress() const =delete
std::enable_if_t< std::is_integral_v< IdxT >, Var< std::add_lvalue_reference_t< ElemType > > > operator[](const Var< IdxT > &Index)
std::remove_extent_t< T > ElemType
Definition Var.h:198
IRType ValueTy
Element type.
Definition Var.h:193
IRValue * loadValue() const
Definition Var.h:209
Definition Var.h:15