xref: /llvm-project/llvm/include/llvm/IR/Operator.h (revision ecbe4d1e360e25c0634a3a62fbd01e8df5bb0c1b)
1 //===-- llvm/Operator.h - Operator utility subclass -------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines various classes for working with Instructions and
10 // ConstantExprs.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_IR_OPERATOR_H
15 #define LLVM_IR_OPERATOR_H
16 
17 #include "llvm/ADT/MapVector.h"
18 #include "llvm/IR/Constants.h"
19 #include "llvm/IR/FMF.h"
20 #include "llvm/IR/GEPNoWrapFlags.h"
21 #include "llvm/IR/Instruction.h"
22 #include "llvm/IR/Type.h"
23 #include "llvm/IR/Value.h"
24 #include "llvm/Support/Casting.h"
25 #include <cstddef>
26 #include <optional>
27 
28 namespace llvm {
29 
30 /// This is a utility class that provides an abstraction for the common
31 /// functionality between Instructions and ConstantExprs.
32 class Operator : public User {
33 public:
34   // The Operator class is intended to be used as a utility, and is never itself
35   // instantiated.
36   Operator() = delete;
37   ~Operator() = delete;
38 
39   void *operator new(size_t s) = delete;
40 
41   /// Return the opcode for this Instruction or ConstantExpr.
42   unsigned getOpcode() const {
43     if (const Instruction *I = dyn_cast<Instruction>(this))
44       return I->getOpcode();
45     return cast<ConstantExpr>(this)->getOpcode();
46   }
47 
48   /// If V is an Instruction or ConstantExpr, return its opcode.
49   /// Otherwise return UserOp1.
50   static unsigned getOpcode(const Value *V) {
51     if (const Instruction *I = dyn_cast<Instruction>(V))
52       return I->getOpcode();
53     if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(V))
54       return CE->getOpcode();
55     return Instruction::UserOp1;
56   }
57 
58   static bool classof(const Instruction *) { return true; }
59   static bool classof(const ConstantExpr *) { return true; }
60   static bool classof(const Value *V) {
61     return isa<Instruction>(V) || isa<ConstantExpr>(V);
62   }
63 
64   /// Return true if this operator has flags which may cause this operator
65   /// to evaluate to poison despite having non-poison inputs.
66   bool hasPoisonGeneratingFlags() const;
67 
68   /// Return true if this operator has poison-generating flags,
69   /// return attributes or metadata. The latter two is only possible for
70   /// instructions.
71   bool hasPoisonGeneratingAnnotations() const;
72 };
73 
74 /// Utility class for integer operators which may exhibit overflow - Add, Sub,
75 /// Mul, and Shl. It does not include SDiv, despite that operator having the
76 /// potential for overflow.
77 class OverflowingBinaryOperator : public Operator {
78 public:
79   enum {
80     AnyWrap        = 0,
81     NoUnsignedWrap = (1 << 0),
82     NoSignedWrap   = (1 << 1)
83   };
84 
85 private:
86   friend class Instruction;
87   friend class ConstantExpr;
88 
89   void setHasNoUnsignedWrap(bool B) {
90     SubclassOptionalData =
91       (SubclassOptionalData & ~NoUnsignedWrap) | (B * NoUnsignedWrap);
92   }
93   void setHasNoSignedWrap(bool B) {
94     SubclassOptionalData =
95       (SubclassOptionalData & ~NoSignedWrap) | (B * NoSignedWrap);
96   }
97 
98 public:
99   /// Transparently provide more efficient getOperand methods.
100   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
101 
102   /// Test whether this operation is known to never
103   /// undergo unsigned overflow, aka the nuw property.
104   bool hasNoUnsignedWrap() const {
105     return SubclassOptionalData & NoUnsignedWrap;
106   }
107 
108   /// Test whether this operation is known to never
109   /// undergo signed overflow, aka the nsw property.
110   bool hasNoSignedWrap() const {
111     return (SubclassOptionalData & NoSignedWrap) != 0;
112   }
113 
114   /// Returns the no-wrap kind of the operation.
115   unsigned getNoWrapKind() const {
116     unsigned NoWrapKind = 0;
117     if (hasNoUnsignedWrap())
118       NoWrapKind |= NoUnsignedWrap;
119 
120     if (hasNoSignedWrap())
121       NoWrapKind |= NoSignedWrap;
122 
123     return NoWrapKind;
124   }
125 
126   /// Return true if the instruction is commutative
127   bool isCommutative() const { return Instruction::isCommutative(getOpcode()); }
128 
129   static bool classof(const Instruction *I) {
130     return I->getOpcode() == Instruction::Add ||
131            I->getOpcode() == Instruction::Sub ||
132            I->getOpcode() == Instruction::Mul ||
133            I->getOpcode() == Instruction::Shl;
134   }
135   static bool classof(const ConstantExpr *CE) {
136     return CE->getOpcode() == Instruction::Add ||
137            CE->getOpcode() == Instruction::Sub ||
138            CE->getOpcode() == Instruction::Mul ||
139            CE->getOpcode() == Instruction::Shl;
140   }
141   static bool classof(const Value *V) {
142     return (isa<Instruction>(V) && classof(cast<Instruction>(V))) ||
143            (isa<ConstantExpr>(V) && classof(cast<ConstantExpr>(V)));
144   }
145 };
146 
147 template <>
148 struct OperandTraits<OverflowingBinaryOperator>
149     : public FixedNumOperandTraits<OverflowingBinaryOperator, 2> {};
150 
151 DEFINE_TRANSPARENT_OPERAND_ACCESSORS(OverflowingBinaryOperator, Value)
152 
153 /// A udiv or sdiv instruction, which can be marked as "exact",
154 /// indicating that no bits are destroyed.
155 class PossiblyExactOperator : public Operator {
156 public:
157   enum {
158     IsExact = (1 << 0)
159   };
160 
161 private:
162   friend class Instruction;
163   friend class ConstantExpr;
164 
165   void setIsExact(bool B) {
166     SubclassOptionalData = (SubclassOptionalData & ~IsExact) | (B * IsExact);
167   }
168 
169 public:
170   /// Transparently provide more efficient getOperand methods.
171   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
172 
173   /// Test whether this division is known to be exact, with zero remainder.
174   bool isExact() const {
175     return SubclassOptionalData & IsExact;
176   }
177 
178   static bool isPossiblyExactOpcode(unsigned OpC) {
179     return OpC == Instruction::SDiv ||
180            OpC == Instruction::UDiv ||
181            OpC == Instruction::AShr ||
182            OpC == Instruction::LShr;
183   }
184 
185   static bool classof(const ConstantExpr *CE) {
186     return isPossiblyExactOpcode(CE->getOpcode());
187   }
188   static bool classof(const Instruction *I) {
189     return isPossiblyExactOpcode(I->getOpcode());
190   }
191   static bool classof(const Value *V) {
192     return (isa<Instruction>(V) && classof(cast<Instruction>(V))) ||
193            (isa<ConstantExpr>(V) && classof(cast<ConstantExpr>(V)));
194   }
195 };
196 
197 template <>
198 struct OperandTraits<PossiblyExactOperator>
199     : public FixedNumOperandTraits<PossiblyExactOperator, 2> {};
200 
201 DEFINE_TRANSPARENT_OPERAND_ACCESSORS(PossiblyExactOperator, Value)
202 
203 /// Utility class for floating point operations which can have
204 /// information about relaxed accuracy requirements attached to them.
205 class FPMathOperator : public Operator {
206 private:
207   friend class Instruction;
208 
209   /// 'Fast' means all bits are set.
210   void setFast(bool B) {
211     setHasAllowReassoc(B);
212     setHasNoNaNs(B);
213     setHasNoInfs(B);
214     setHasNoSignedZeros(B);
215     setHasAllowReciprocal(B);
216     setHasAllowContract(B);
217     setHasApproxFunc(B);
218   }
219 
220   void setHasAllowReassoc(bool B) {
221     SubclassOptionalData =
222     (SubclassOptionalData & ~FastMathFlags::AllowReassoc) |
223     (B * FastMathFlags::AllowReassoc);
224   }
225 
226   void setHasNoNaNs(bool B) {
227     SubclassOptionalData =
228       (SubclassOptionalData & ~FastMathFlags::NoNaNs) |
229       (B * FastMathFlags::NoNaNs);
230   }
231 
232   void setHasNoInfs(bool B) {
233     SubclassOptionalData =
234       (SubclassOptionalData & ~FastMathFlags::NoInfs) |
235       (B * FastMathFlags::NoInfs);
236   }
237 
238   void setHasNoSignedZeros(bool B) {
239     SubclassOptionalData =
240       (SubclassOptionalData & ~FastMathFlags::NoSignedZeros) |
241       (B * FastMathFlags::NoSignedZeros);
242   }
243 
244   void setHasAllowReciprocal(bool B) {
245     SubclassOptionalData =
246       (SubclassOptionalData & ~FastMathFlags::AllowReciprocal) |
247       (B * FastMathFlags::AllowReciprocal);
248   }
249 
250   void setHasAllowContract(bool B) {
251     SubclassOptionalData =
252         (SubclassOptionalData & ~FastMathFlags::AllowContract) |
253         (B * FastMathFlags::AllowContract);
254   }
255 
256   void setHasApproxFunc(bool B) {
257     SubclassOptionalData =
258         (SubclassOptionalData & ~FastMathFlags::ApproxFunc) |
259         (B * FastMathFlags::ApproxFunc);
260   }
261 
262   /// Convenience function for setting multiple fast-math flags.
263   /// FMF is a mask of the bits to set.
264   void setFastMathFlags(FastMathFlags FMF) {
265     SubclassOptionalData |= FMF.Flags;
266   }
267 
268   /// Convenience function for copying all fast-math flags.
269   /// All values in FMF are transferred to this operator.
270   void copyFastMathFlags(FastMathFlags FMF) {
271     SubclassOptionalData = FMF.Flags;
272   }
273 
274   /// Returns true if `Ty` is composed of a single kind of float-poing type
275   /// (possibly repeated within an aggregate).
276   static bool isComposedOfHomogeneousFloatingPointTypes(Type *Ty) {
277     if (auto *StructTy = dyn_cast<StructType>(Ty)) {
278       if (!StructTy->isLiteral() || !StructTy->containsHomogeneousTypes())
279         return false;
280       Ty = StructTy->elements().front();
281     } else if (auto *ArrayTy = dyn_cast<ArrayType>(Ty)) {
282       do {
283         Ty = ArrayTy->getElementType();
284       } while ((ArrayTy = dyn_cast<ArrayType>(Ty)));
285     }
286     return Ty->isFPOrFPVectorTy();
287   };
288 
289 public:
290   /// Test if this operation allows all non-strict floating-point transforms.
291   bool isFast() const {
292     return ((SubclassOptionalData & FastMathFlags::AllowReassoc) != 0 &&
293             (SubclassOptionalData & FastMathFlags::NoNaNs) != 0 &&
294             (SubclassOptionalData & FastMathFlags::NoInfs) != 0 &&
295             (SubclassOptionalData & FastMathFlags::NoSignedZeros) != 0 &&
296             (SubclassOptionalData & FastMathFlags::AllowReciprocal) != 0 &&
297             (SubclassOptionalData & FastMathFlags::AllowContract) != 0 &&
298             (SubclassOptionalData & FastMathFlags::ApproxFunc) != 0);
299   }
300 
301   /// Test if this operation may be simplified with reassociative transforms.
302   bool hasAllowReassoc() const {
303     return (SubclassOptionalData & FastMathFlags::AllowReassoc) != 0;
304   }
305 
306   /// Test if this operation's arguments and results are assumed not-NaN.
307   bool hasNoNaNs() const {
308     return (SubclassOptionalData & FastMathFlags::NoNaNs) != 0;
309   }
310 
311   /// Test if this operation's arguments and results are assumed not-infinite.
312   bool hasNoInfs() const {
313     return (SubclassOptionalData & FastMathFlags::NoInfs) != 0;
314   }
315 
316   /// Test if this operation can ignore the sign of zero.
317   bool hasNoSignedZeros() const {
318     return (SubclassOptionalData & FastMathFlags::NoSignedZeros) != 0;
319   }
320 
321   /// Test if this operation can use reciprocal multiply instead of division.
322   bool hasAllowReciprocal() const {
323     return (SubclassOptionalData & FastMathFlags::AllowReciprocal) != 0;
324   }
325 
326   /// Test if this operation can be floating-point contracted (FMA).
327   bool hasAllowContract() const {
328     return (SubclassOptionalData & FastMathFlags::AllowContract) != 0;
329   }
330 
331   /// Test if this operation allows approximations of math library functions or
332   /// intrinsics.
333   bool hasApproxFunc() const {
334     return (SubclassOptionalData & FastMathFlags::ApproxFunc) != 0;
335   }
336 
337   /// Convenience function for getting all the fast-math flags
338   FastMathFlags getFastMathFlags() const {
339     return FastMathFlags(SubclassOptionalData);
340   }
341 
342   /// Get the maximum error permitted by this operation in ULPs. An accuracy of
343   /// 0.0 means that the operation should be performed with the default
344   /// precision.
345   float getFPAccuracy() const;
346 
347   /// Returns true if `Ty` is a supported floating-point type for phi, select,
348   /// or call FPMathOperators.
349   static bool isSupportedFloatingPointType(Type *Ty) {
350     return Ty->isFPOrFPVectorTy() ||
351            isComposedOfHomogeneousFloatingPointTypes(Ty);
352   }
353 
354   static bool classof(const Value *V) {
355     unsigned Opcode;
356     if (auto *I = dyn_cast<Instruction>(V))
357       Opcode = I->getOpcode();
358     else
359       return false;
360 
361     switch (Opcode) {
362     case Instruction::FNeg:
363     case Instruction::FAdd:
364     case Instruction::FSub:
365     case Instruction::FMul:
366     case Instruction::FDiv:
367     case Instruction::FRem:
368     case Instruction::FPTrunc:
369     case Instruction::FPExt:
370     // FIXME: To clean up and correct the semantics of fast-math-flags, FCmp
371     //        should not be treated as a math op, but the other opcodes should.
372     //        This would make things consistent with Select/PHI (FP value type
373     //        determines whether they are math ops and, therefore, capable of
374     //        having fast-math-flags).
375     case Instruction::FCmp:
376       return true;
377     case Instruction::PHI:
378     case Instruction::Select:
379     case Instruction::Call: {
380       return isSupportedFloatingPointType(V->getType());
381     }
382     default:
383       return false;
384     }
385   }
386 };
387 
388 /// A helper template for defining operators for individual opcodes.
389 template<typename SuperClass, unsigned Opc>
390 class ConcreteOperator : public SuperClass {
391 public:
392   static bool classof(const Instruction *I) {
393     return I->getOpcode() == Opc;
394   }
395   static bool classof(const ConstantExpr *CE) {
396     return CE->getOpcode() == Opc;
397   }
398   static bool classof(const Value *V) {
399     return (isa<Instruction>(V) && classof(cast<Instruction>(V))) ||
400            (isa<ConstantExpr>(V) && classof(cast<ConstantExpr>(V)));
401   }
402 };
403 
404 class AddOperator
405   : public ConcreteOperator<OverflowingBinaryOperator, Instruction::Add> {
406 };
407 class SubOperator
408   : public ConcreteOperator<OverflowingBinaryOperator, Instruction::Sub> {
409 };
410 class MulOperator
411   : public ConcreteOperator<OverflowingBinaryOperator, Instruction::Mul> {
412 };
413 class ShlOperator
414   : public ConcreteOperator<OverflowingBinaryOperator, Instruction::Shl> {
415 };
416 
417 class AShrOperator
418   : public ConcreteOperator<PossiblyExactOperator, Instruction::AShr> {
419 };
420 class LShrOperator
421   : public ConcreteOperator<PossiblyExactOperator, Instruction::LShr> {
422 };
423 
424 class GEPOperator
425     : public ConcreteOperator<Operator, Instruction::GetElementPtr> {
426 public:
427   /// Transparently provide more efficient getOperand methods.
428   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
429 
430   GEPNoWrapFlags getNoWrapFlags() const {
431     return GEPNoWrapFlags::fromRaw(SubclassOptionalData);
432   }
433 
434   /// Test whether this is an inbounds GEP, as defined by LangRef.html.
435   bool isInBounds() const { return getNoWrapFlags().isInBounds(); }
436 
437   bool hasNoUnsignedSignedWrap() const {
438     return getNoWrapFlags().hasNoUnsignedSignedWrap();
439   }
440 
441   bool hasNoUnsignedWrap() const {
442     return getNoWrapFlags().hasNoUnsignedWrap();
443   }
444 
445   /// Returns the offset of the index with an inrange attachment, or
446   /// std::nullopt if none.
447   std::optional<ConstantRange> getInRange() const;
448 
449   inline op_iterator       idx_begin()       { return op_begin()+1; }
450   inline const_op_iterator idx_begin() const { return op_begin()+1; }
451   inline op_iterator       idx_end()         { return op_end(); }
452   inline const_op_iterator idx_end()   const { return op_end(); }
453 
454   inline iterator_range<op_iterator> indices() {
455     return make_range(idx_begin(), idx_end());
456   }
457 
458   inline iterator_range<const_op_iterator> indices() const {
459     return make_range(idx_begin(), idx_end());
460   }
461 
462   Value *getPointerOperand() {
463     return getOperand(0);
464   }
465   const Value *getPointerOperand() const {
466     return getOperand(0);
467   }
468   static unsigned getPointerOperandIndex() {
469     return 0U;                      // get index for modifying correct operand
470   }
471 
472   /// Method to return the pointer operand as a PointerType.
473   Type *getPointerOperandType() const {
474     return getPointerOperand()->getType();
475   }
476 
477   Type *getSourceElementType() const;
478   Type *getResultElementType() const;
479 
480   /// Method to return the address space of the pointer operand.
481   unsigned getPointerAddressSpace() const {
482     return getPointerOperandType()->getPointerAddressSpace();
483   }
484 
485   unsigned getNumIndices() const {  // Note: always non-negative
486     return getNumOperands() - 1;
487   }
488 
489   bool hasIndices() const {
490     return getNumOperands() > 1;
491   }
492 
493   /// Return true if all of the indices of this GEP are zeros.
494   /// If so, the result pointer and the first operand have the same
495   /// value, just potentially different types.
496   bool hasAllZeroIndices() const {
497     for (const_op_iterator I = idx_begin(), E = idx_end(); I != E; ++I) {
498       if (ConstantInt *C = dyn_cast<ConstantInt>(I))
499         if (C->isZero())
500           continue;
501       return false;
502     }
503     return true;
504   }
505 
506   /// Return true if all of the indices of this GEP are constant integers.
507   /// If so, the result pointer and the first operand have
508   /// a constant offset between them.
509   bool hasAllConstantIndices() const {
510     for (const_op_iterator I = idx_begin(), E = idx_end(); I != E; ++I) {
511       if (!isa<ConstantInt>(I))
512         return false;
513     }
514     return true;
515   }
516 
517   unsigned countNonConstantIndices() const {
518     return count_if(indices(), [](const Use& use) {
519         return !isa<ConstantInt>(*use);
520       });
521   }
522 
523   /// Compute the maximum alignment that this GEP is garranteed to preserve.
524   Align getMaxPreservedAlignment(const DataLayout &DL) const;
525 
526   /// Accumulate the constant address offset of this GEP if possible.
527   ///
528   /// This routine accepts an APInt into which it will try to accumulate the
529   /// constant offset of this GEP.
530   ///
531   /// If \p ExternalAnalysis is provided it will be used to calculate a offset
532   /// when a operand of GEP is not constant.
533   /// For example, for a value \p ExternalAnalysis might try to calculate a
534   /// lower bound. If \p ExternalAnalysis is successful, it should return true.
535   ///
536   /// If the \p ExternalAnalysis returns false or the value returned by \p
537   /// ExternalAnalysis results in a overflow/underflow, this routine returns
538   /// false and the value of the offset APInt is undefined (it is *not*
539   /// preserved!).
540   ///
541   /// The APInt passed into this routine must be at exactly as wide as the
542   /// IntPtr type for the address space of the base GEP pointer.
543   bool accumulateConstantOffset(
544       const DataLayout &DL, APInt &Offset,
545       function_ref<bool(Value &, APInt &)> ExternalAnalysis = nullptr) const;
546 
547   static bool accumulateConstantOffset(
548       Type *SourceType, ArrayRef<const Value *> Index, const DataLayout &DL,
549       APInt &Offset,
550       function_ref<bool(Value &, APInt &)> ExternalAnalysis = nullptr);
551 
552   /// Collect the offset of this GEP as a map of Values to their associated
553   /// APInt multipliers, as well as a total Constant Offset.
554   bool collectOffset(const DataLayout &DL, unsigned BitWidth,
555                      SmallMapVector<Value *, APInt, 4> &VariableOffsets,
556                      APInt &ConstantOffset) const;
557 };
558 
559 template <>
560 struct OperandTraits<GEPOperator> : public VariadicOperandTraits<GEPOperator> {
561 };
562 
563 DEFINE_TRANSPARENT_OPERAND_ACCESSORS(GEPOperator, Value)
564 
565 class PtrToIntOperator
566     : public ConcreteOperator<Operator, Instruction::PtrToInt> {
567   friend class PtrToInt;
568   friend class ConstantExpr;
569 
570 public:
571   /// Transparently provide more efficient getOperand methods.
572   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
573 
574   Value *getPointerOperand() {
575     return getOperand(0);
576   }
577   const Value *getPointerOperand() const {
578     return getOperand(0);
579   }
580 
581   static unsigned getPointerOperandIndex() {
582     return 0U;                      // get index for modifying correct operand
583   }
584 
585   /// Method to return the pointer operand as a PointerType.
586   Type *getPointerOperandType() const {
587     return getPointerOperand()->getType();
588   }
589 
590   /// Method to return the address space of the pointer operand.
591   unsigned getPointerAddressSpace() const {
592     return cast<PointerType>(getPointerOperandType())->getAddressSpace();
593   }
594 };
595 
596 template <>
597 struct OperandTraits<PtrToIntOperator>
598     : public FixedNumOperandTraits<PtrToIntOperator, 1> {};
599 
600 DEFINE_TRANSPARENT_OPERAND_ACCESSORS(PtrToIntOperator, Value)
601 
602 class BitCastOperator
603     : public ConcreteOperator<Operator, Instruction::BitCast> {
604   friend class BitCastInst;
605   friend class ConstantExpr;
606 
607 public:
608   /// Transparently provide more efficient getOperand methods.
609   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
610 
611   Type *getSrcTy() const {
612     return getOperand(0)->getType();
613   }
614 
615   Type *getDestTy() const {
616     return getType();
617   }
618 };
619 
620 template <>
621 struct OperandTraits<BitCastOperator>
622     : public FixedNumOperandTraits<BitCastOperator, 1> {};
623 
624 DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BitCastOperator, Value)
625 
626 class AddrSpaceCastOperator
627     : public ConcreteOperator<Operator, Instruction::AddrSpaceCast> {
628   friend class AddrSpaceCastInst;
629   friend class ConstantExpr;
630 
631 public:
632   /// Transparently provide more efficient getOperand methods.
633   DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
634 
635   Value *getPointerOperand() { return getOperand(0); }
636 
637   const Value *getPointerOperand() const { return getOperand(0); }
638 
639   unsigned getSrcAddressSpace() const {
640     return getPointerOperand()->getType()->getPointerAddressSpace();
641   }
642 
643   unsigned getDestAddressSpace() const {
644     return getType()->getPointerAddressSpace();
645   }
646 };
647 
648 template <>
649 struct OperandTraits<AddrSpaceCastOperator>
650     : public FixedNumOperandTraits<AddrSpaceCastOperator, 1> {};
651 
652 DEFINE_TRANSPARENT_OPERAND_ACCESSORS(AddrSpaceCastOperator, Value)
653 
654 } // end namespace llvm
655 
656 #endif // LLVM_IR_OPERATOR_H
657