*** empty log message ***
[oota-llvm.git] / include / llvm / CodeGen / MachineInstr.h
1 //===-- llvm/CodeGen/MachineInstr.h - MachineInstr class ---------*- C++ -*--=//
2 //
3 // This file contains the declaration of the MachineInstr class, which is the
4 // basic representation for all target dependant machine instructions used by
5 // the back end.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_CODEGEN_MACHINEINSTR_H
10 #define LLVM_CODEGEN_MACHINEINSTR_H
11
12 #include "llvm/Annotation.h"
13 #include "llvm/Target/MRegisterInfo.h"
14 #include "Support/iterator"
15 #include "Support/NonCopyable.h"
16 #include <vector>
17 class Value;
18 class Function;
19 class MachineBasicBlock;
20 class TargetMachine;
21
22 typedef int MachineOpCode;
23
24 /// MOTy - MachineOperandType - This namespace contains an enum that describes
25 /// how the machine operand is used by the instruction: is it read, defined, or
26 /// both?  Note that the MachineInstr/Operator class currently uses bool
27 /// arguments to represent this information instead of an enum.  Eventually this
28 /// should change over to use this _easier to read_ representation instead.
29 ///
30 namespace MOTy {
31   enum UseType {
32     Use,             /// This machine operand is only read by the instruction
33     Def,             /// This machine operand is only written by the instruction
34     UseAndDef        /// This machine operand is read AND written
35   };
36 }
37
38 //---------------------------------------------------------------------------
39 // class MachineOperand 
40 // 
41 // Purpose:
42 //   Representation of each machine instruction operand.
43 //   This class is designed so that you can allocate a vector of operands
44 //   first and initialize each one later.
45 //
46 //   E.g, for this VM instruction:
47 //              ptr = alloca type, numElements
48 //   we generate 2 machine instructions on the SPARC:
49 // 
50 //              mul Constant, Numelements -> Reg
51 //              add %sp, Reg -> Ptr
52 // 
53 //   Each instruction has 3 operands, listed above.  Of those:
54 //   -  Reg, NumElements, and Ptr are of operand type MO_Register.
55 //   -  Constant is of operand type MO_SignExtendedImmed on the SPARC.
56 //      
57 //   For the register operands, the virtual register type is as follows:
58 //      
59 //   -  Reg will be of virtual register type MO_MInstrVirtualReg.  The field
60 //      MachineInstr* minstr will point to the instruction that computes reg.
61 // 
62 //   -  %sp will be of virtual register type MO_MachineReg.
63 //      The field regNum identifies the machine register.
64 // 
65 //   -  NumElements will be of virtual register type MO_VirtualReg.
66 //      The field Value* value identifies the value.
67 // 
68 //   -  Ptr will also be of virtual register type MO_VirtualReg.
69 //      Again, the field Value* value identifies the value.
70 // 
71 //---------------------------------------------------------------------------
72
73 class MachineOperand {
74 public:
75   enum MachineOperandType {
76     MO_VirtualRegister,         // virtual register for *value
77     MO_MachineRegister,         // pre-assigned machine register `regNum'
78     MO_CCRegister,
79     MO_SignExtendedImmed,
80     MO_UnextendedImmed,
81     MO_PCRelativeDisp,
82     MO_MachineBasicBlock,       // MachineBasicBlock reference
83     MO_FrameIndex,              // Abstract Stack Frame Index
84   };
85   
86 private:
87   // Bit fields of the flags variable used for different operand properties
88   static const char DEFFLAG    = 0x01; // this is a def of the operand
89   static const char DEFUSEFLAG = 0x02; // this is both a def and a use
90   static const char HIFLAG32   = 0x04; // operand is %hi32(value_or_immedVal)
91   static const char LOFLAG32   = 0x08; // operand is %lo32(value_or_immedVal)
92   static const char HIFLAG64   = 0x10; // operand is %hi64(value_or_immedVal)
93   static const char LOFLAG64   = 0x20; // operand is %lo64(value_or_immedVal)
94
95   static const char USEDEFMASK = 0x03;
96   
97 private:
98   union {
99     Value*      value;          // BasicBlockVal for a label operand.
100                                 // ConstantVal for a non-address immediate.
101                                 // Virtual register for an SSA operand,
102                                 // including hidden operands required for
103                                 // the generated machine code.     
104     int64_t immedVal;           // constant value for an explicit constant
105
106     MachineBasicBlock *MBB;     // For MO_MachineBasicBlock type
107   };
108
109   char flags;                   // see bit field definitions above
110   MachineOperandType opType:8;  // Pack into 8 bits efficiently after flags.
111   int regNum;                   // register number for an explicit register
112                                 // will be set for a value after reg allocation
113 private:
114   MachineOperand()
115     : immedVal(0),
116       flags(0),
117       opType(MO_VirtualRegister),
118       regNum(-1) {}
119
120   MachineOperand(int64_t ImmVal, MachineOperandType OpTy)
121     : immedVal(ImmVal),
122       flags(0),
123       opType(OpTy),
124       regNum(-1) {}
125
126   MachineOperand(int Reg, MachineOperandType OpTy, MOTy::UseType UseTy)
127     : immedVal(0),
128       opType(OpTy),
129       regNum(Reg) {
130     switch (UseTy) {
131     case MOTy::Use:       flags = 0; break;
132     case MOTy::Def:       flags = DEFFLAG; break;
133     case MOTy::UseAndDef: flags = DEFUSEFLAG; break;
134     default: assert(0 && "Invalid value for UseTy!");
135     }
136   }
137
138   MachineOperand(Value *V, MachineOperandType OpTy, MOTy::UseType UseTy) 
139     : value(V), opType(OpTy), regNum(-1) {
140     switch (UseTy) {
141     case MOTy::Use:       flags = 0; break;
142     case MOTy::Def:       flags = DEFFLAG; break;
143     case MOTy::UseAndDef: flags = DEFUSEFLAG; break;
144     default: assert(0 && "Invalid value for UseTy!");
145     }
146   }
147
148   MachineOperand(MachineBasicBlock *mbb)
149     : MBB(mbb), flags(0), opType(MO_MachineBasicBlock), regNum(-1) {}
150
151 public:
152   MachineOperand(const MachineOperand &M)
153     : immedVal(M.immedVal),
154       flags(M.flags),
155       opType(M.opType),
156       regNum(M.regNum) {}
157
158   ~MachineOperand() {}
159   
160   // Accessor methods.  Caller is responsible for checking the
161   // operand type before invoking the corresponding accessor.
162   // 
163   MachineOperandType getType() const { return opType; }
164
165
166   // This is to finally stop caring whether we have a virtual or machine
167   // register -- an easier interface is to simply call both virtual and machine
168   // registers essentially the same, yet be able to distinguish when
169   // necessary. Thus the instruction selector can just add registers without
170   // abandon, and the register allocator won't be confused.
171   bool isVirtualRegister() const {
172     return (opType == MO_VirtualRegister || opType == MO_MachineRegister) 
173       && regNum >= MRegisterInfo::FirstVirtualRegister;
174   }
175   bool isPhysicalRegister() const {
176     return (opType == MO_VirtualRegister || opType == MO_MachineRegister) 
177       && regNum < MRegisterInfo::FirstVirtualRegister;
178   }
179   bool isRegister() const { return isVirtualRegister() || isPhysicalRegister();}
180   bool isMachineRegister() const { return !isVirtualRegister(); }
181   bool isMachineBasicBlock() const { return opType == MO_MachineBasicBlock; }
182   bool isPCRelativeDisp() const { return opType == MO_PCRelativeDisp; }
183   bool isImmediate() const {
184     return opType == MO_SignExtendedImmed || opType == MO_UnextendedImmed;
185   }
186   bool isFrameIndex() const { return opType == MO_FrameIndex; }
187
188   Value* getVRegValue() const {
189     assert(opType == MO_VirtualRegister || opType == MO_CCRegister || 
190            isPCRelativeDisp());
191     return value;
192   }
193   Value* getVRegValueOrNull() const {
194     return (opType == MO_VirtualRegister || opType == MO_CCRegister || 
195             isPCRelativeDisp()) ? value : NULL;
196   }
197   int getMachineRegNum() const {
198     assert(opType == MO_MachineRegister);
199     return regNum;
200   }
201   int64_t getImmedValue() const { assert(isImmediate()); return immedVal; }
202   MachineBasicBlock *getMachineBasicBlock() const {
203     assert(isMachineBasicBlock() && "Can't get MBB in non-MBB operand!");
204     return MBB;
205   }
206   unsigned getFrameIndex() const { assert(isFrameIndex()); return immedVal; }
207
208   bool          opIsUse         () const { return (flags & USEDEFMASK) == 0; }
209   bool          opIsDef         () const { return flags & DEFFLAG; }
210   bool          opIsDefAndUse   () const { return flags & DEFUSEFLAG; }
211   bool          opHiBits32      () const { return flags & HIFLAG32; }
212   bool          opLoBits32      () const { return flags & LOFLAG32; }
213   bool          opHiBits64      () const { return flags & HIFLAG64; }
214   bool          opLoBits64      () const { return flags & LOFLAG64; }
215
216   // used to check if a machine register has been allocated to this operand
217   bool hasAllocatedReg() const {
218     return (regNum >= 0 &&
219             (opType == MO_VirtualRegister || opType == MO_CCRegister || 
220              opType == MO_MachineRegister));
221   }
222
223   // used to get the reg number if when one is allocated
224   int getAllocatedRegNum() const {
225     assert(opType == MO_VirtualRegister || opType == MO_CCRegister || 
226            opType == MO_MachineRegister);
227     return regNum;
228   }
229
230   unsigned getReg() const {
231     assert(hasAllocatedReg() && "Cannot call MachineOperand::getReg()!");
232     return regNum;
233   }    
234   
235   friend std::ostream& operator<<(std::ostream& os, const MachineOperand& mop);
236
237 private:
238
239   // Construction methods needed for fine-grain control.
240   // These must be accessed via coresponding methods in MachineInstr.
241   void markHi32()      { flags |= HIFLAG32; }
242   void markLo32()      { flags |= LOFLAG32; }
243   void markHi64()      { flags |= HIFLAG64; }
244   void markLo64()      { flags |= LOFLAG64; }
245   
246   // Replaces the Value with its corresponding physical register after
247   // register allocation is complete
248   void setRegForValue(int reg) {
249     assert(opType == MO_VirtualRegister || opType == MO_CCRegister || 
250            opType == MO_MachineRegister);
251     regNum = reg;
252   }
253   
254   friend class MachineInstr;
255 };
256
257
258 //---------------------------------------------------------------------------
259 // class MachineInstr 
260 // 
261 // Purpose:
262 //   Representation of each machine instruction.
263 // 
264 //   MachineOpCode must be an enum, defined separately for each target.
265 //   E.g., It is defined in SparcInstructionSelection.h for the SPARC.
266 // 
267 //  There are 2 kinds of operands:
268 // 
269 //  (1) Explicit operands of the machine instruction in vector operands[] 
270 // 
271 //  (2) "Implicit operands" are values implicitly used or defined by the
272 //      machine instruction, such as arguments to a CALL, return value of
273 //      a CALL (if any), and return value of a RETURN.
274 //---------------------------------------------------------------------------
275
276 class MachineInstr: public NonCopyable {      // Disable copy operations
277
278   MachineOpCode    opCode;              // the opcode
279   std::vector<MachineOperand> operands; // the operands
280   unsigned numImplicitRefs;             // number of implicit operands
281
282   MachineOperand& getImplicitOp(unsigned i) {
283     assert(i < numImplicitRefs && "implicit ref# out of range!");
284     return operands[i + operands.size() - numImplicitRefs];
285   }
286   const MachineOperand& getImplicitOp(unsigned i) const {
287     assert(i < numImplicitRefs && "implicit ref# out of range!");
288     return operands[i + operands.size() - numImplicitRefs];
289   }
290
291   // regsUsed - all machine registers used for this instruction, including regs
292   // used to save values across the instruction.  This is a bitset of registers.
293   std::vector<bool> regsUsed;
294
295   // OperandComplete - Return true if it's illegal to add a new operand
296   bool OperandsComplete() const;
297
298 public:
299   MachineInstr(MachineOpCode Opcode);
300   MachineInstr(MachineOpCode Opcode, unsigned numOperands);
301
302   /// MachineInstr ctor - This constructor only does a _reserve_ of the
303   /// operands, not a resize for them.  It is expected that if you use this that
304   /// you call add* methods below to fill up the operands, instead of the Set
305   /// methods.  Eventually, the "resizing" ctors will be phased out.
306   ///
307   MachineInstr(MachineOpCode Opcode, unsigned numOperands, bool XX, bool YY);
308
309   /// MachineInstr ctor - Work exactly the same as the ctor above, except that
310   /// the MachineInstr is created and added to the end of the specified basic
311   /// block.
312   ///
313   MachineInstr(MachineBasicBlock *MBB, MachineOpCode Opcode, unsigned numOps);
314   
315
316   /// replace - Support to rewrite a machine instruction in place: for now,
317   /// simply replace() and then set new operands with Set.*Operand methods
318   /// below.
319   /// 
320   void replace(MachineOpCode Opcode, unsigned numOperands);
321   
322   // The opcode.
323   // 
324   const MachineOpCode getOpcode() const { return opCode; }
325   const MachineOpCode getOpCode() const { return opCode; }
326
327   //
328   // Information about explicit operands of the instruction
329   // 
330   unsigned getNumOperands() const { return operands.size() - numImplicitRefs; }
331   
332   const MachineOperand& getOperand(unsigned i) const {
333     assert(i < getNumOperands() && "getOperand() out of range!");
334     return operands[i];
335   }
336   MachineOperand& getOperand(unsigned i) {
337     assert(i < getNumOperands() && "getOperand() out of range!");
338     return operands[i];
339   }
340
341   MachineOperand::MachineOperandType getOperandType(unsigned i) const {
342     return getOperand(i).getType();
343   }
344
345   bool operandIsDefined(unsigned i) const {
346     return getOperand(i).opIsDef();
347   }
348
349   bool operandIsDefinedAndUsed(unsigned i) const {
350     return getOperand(i).opIsDefAndUse();
351   }
352
353   //
354   // Information about implicit operands of the instruction
355   // 
356   unsigned getNumImplicitRefs() const{ return numImplicitRefs; }
357   
358   const Value* getImplicitRef(unsigned i) const {
359     return getImplicitOp(i).getVRegValue();
360   }
361   Value* getImplicitRef(unsigned i) {
362     return getImplicitOp(i).getVRegValue();
363   }
364
365   bool implicitRefIsDefined(unsigned i) const {
366     return getImplicitOp(i).opIsDef();
367   }
368   bool implicitRefIsDefinedAndUsed(unsigned i) const {
369     return getImplicitOp(i).opIsDefAndUse();
370   }
371   inline void addImplicitRef    (Value* V,
372                                  bool isDef=false,bool isDefAndUse=false);
373   inline void setImplicitRef    (unsigned i, Value* V,
374                                  bool isDef=false, bool isDefAndUse=false);
375
376   //
377   // Information about registers used in this instruction
378   // 
379   const std::vector<bool> &getRegsUsed() const { return regsUsed; }
380   
381   // insertUsedReg - Add a register to the Used registers set...
382   void insertUsedReg(unsigned Reg) {
383     if (Reg >= regsUsed.size())
384       regsUsed.resize(Reg+1);
385     regsUsed[Reg] = true;
386   }
387
388   //
389   // Debugging support
390   //
391   void print(std::ostream &OS, const TargetMachine &TM) const;
392   void dump() const;
393   friend std::ostream& operator<<(std::ostream& os, const MachineInstr& minstr);
394
395   //
396   // Define iterators to access the Value operands of the Machine Instruction.
397   // Note that these iterators only enumerate the explicit operands.
398   // begin() and end() are defined to produce these iterators...
399   //
400   template<class _MI, class _V> class ValOpIterator;
401   typedef ValOpIterator<const MachineInstr*,const Value*> const_val_op_iterator;
402   typedef ValOpIterator<      MachineInstr*,      Value*> val_op_iterator;
403
404   // Access to set the operands when building the machine instruction
405   // 
406   void SetMachineOperandVal     (unsigned i,
407                                  MachineOperand::MachineOperandType operandType,
408                                  Value* V,
409                                  bool isDef=false,
410                                  bool isDefAndUse=false);
411
412   void SetMachineOperandConst   (unsigned i,
413                                  MachineOperand::MachineOperandType operandType,
414                                  int64_t intValue);
415
416   void SetMachineOperandReg     (unsigned i,
417                                  int regNum,
418                                  bool isDef=false);
419
420   //===--------------------------------------------------------------------===//
421   // Accessors to add operands when building up machine instructions
422   //
423
424   /// addRegOperand - Add a MO_VirtualRegister operand to the end of the
425   /// operands list...
426   ///
427   void addRegOperand(Value *V, bool isDef, bool isDefAndUse=false) {
428     assert(!OperandsComplete() &&
429            "Trying to add an operand to a machine instr that is already done!");
430     operands.push_back(MachineOperand(V, MachineOperand::MO_VirtualRegister,
431              !isDef ? MOTy::Use : (isDefAndUse ? MOTy::UseAndDef : MOTy::Def)));
432   }
433
434   void addRegOperand(Value *V, MOTy::UseType UTy = MOTy::Use) {
435     assert(!OperandsComplete() &&
436            "Trying to add an operand to a machine instr that is already done!");
437     operands.push_back(MachineOperand(V, MachineOperand::MO_VirtualRegister,
438                                       UTy));
439   }
440
441   /// addRegOperand - Add a symbolic virtual register reference...
442   ///
443   void addRegOperand(int reg, bool isDef) {
444     assert(!OperandsComplete() &&
445            "Trying to add an operand to a machine instr that is already done!");
446     operands.push_back(MachineOperand(reg, MachineOperand::MO_VirtualRegister,
447                                       isDef ? MOTy::Def : MOTy::Use));
448   }
449
450   /// addRegOperand - Add a symbolic virtual register reference...
451   ///
452   void addRegOperand(int reg, MOTy::UseType UTy = MOTy::Use) {
453     assert(!OperandsComplete() &&
454            "Trying to add an operand to a machine instr that is already done!");
455     operands.push_back(MachineOperand(reg, MachineOperand::MO_VirtualRegister,
456                                       UTy));
457   }
458
459   /// addPCDispOperand - Add a PC relative displacement operand to the MI
460   ///
461   void addPCDispOperand(Value *V) {
462     assert(!OperandsComplete() &&
463            "Trying to add an operand to a machine instr that is already done!");
464     operands.push_back(MachineOperand(V, MachineOperand::MO_PCRelativeDisp,
465                                       MOTy::Use));
466   }
467
468   /// addMachineRegOperand - Add a virtual register operand to this MachineInstr
469   ///
470   void addMachineRegOperand(int reg, bool isDef) {
471     assert(!OperandsComplete() &&
472            "Trying to add an operand to a machine instr that is already done!");
473     operands.push_back(MachineOperand(reg, MachineOperand::MO_MachineRegister,
474                                       isDef ? MOTy::Def : MOTy::Use));
475     insertUsedReg(reg);
476   }
477
478   /// addMachineRegOperand - Add a virtual register operand to this MachineInstr
479   ///
480   void addMachineRegOperand(int reg, MOTy::UseType UTy = MOTy::Use) {
481     assert(!OperandsComplete() &&
482            "Trying to add an operand to a machine instr that is already done!");
483     operands.push_back(MachineOperand(reg, MachineOperand::MO_MachineRegister,
484                                       UTy));
485     insertUsedReg(reg);
486   }
487
488   /// addZeroExtImmOperand - Add a zero extended constant argument to the
489   /// machine instruction.
490   ///
491   void addZeroExtImmOperand(int64_t intValue) {
492     assert(!OperandsComplete() &&
493            "Trying to add an operand to a machine instr that is already done!");
494     operands.push_back(MachineOperand(intValue,
495                                       MachineOperand::MO_UnextendedImmed));
496   }
497
498   /// addSignExtImmOperand - Add a zero extended constant argument to the
499   /// machine instruction.
500   ///
501   void addSignExtImmOperand(int64_t intValue) {
502     assert(!OperandsComplete() &&
503            "Trying to add an operand to a machine instr that is already done!");
504     operands.push_back(MachineOperand(intValue,
505                                       MachineOperand::MO_SignExtendedImmed));
506   }
507
508   void addMachineBasicBlockOperand(MachineBasicBlock *MBB) {
509     assert(!OperandsComplete() &&
510            "Trying to add an operand to a machine instr that is already done!");
511     operands.push_back(MachineOperand(MBB));
512   }
513
514   /// addFrameIndexOperand - Add an abstract frame index to the instruction
515   ///
516   void addFrameIndexOperand(unsigned Idx) {
517     assert(!OperandsComplete() &&
518            "Trying to add an operand to a machine instr that is already done!");
519     operands.push_back(MachineOperand(Idx, MachineOperand::MO_FrameIndex));
520   }
521
522   unsigned substituteValue(const Value* oldVal, Value* newVal,
523                            bool defsOnly = true);
524
525   void setOperandHi32(unsigned i) { operands[i].markHi32(); }
526   void setOperandLo32(unsigned i) { operands[i].markLo32(); }
527   void setOperandHi64(unsigned i) { operands[i].markHi64(); }
528   void setOperandLo64(unsigned i) { operands[i].markLo64(); }
529   
530   
531   // SetRegForOperand - Replaces the Value for the operand with its allocated
532   // physical register after register allocation is complete.
533   // 
534   void SetRegForOperand(unsigned i, int regNum);
535
536   //
537   // Iterator to enumerate machine operands.
538   // 
539   template<class MITy, class VTy>
540   class ValOpIterator : public forward_iterator<VTy, ptrdiff_t> {
541     unsigned i;
542     MITy MI;
543     
544     void skipToNextVal() {
545       while (i < MI->getNumOperands() &&
546              !( (MI->getOperandType(i) == MachineOperand::MO_VirtualRegister ||
547                  MI->getOperandType(i) == MachineOperand::MO_CCRegister)
548                 && MI->getOperand(i).getVRegValue() != 0))
549         ++i;
550     }
551   
552     inline ValOpIterator(MITy mi, unsigned I) : i(I), MI(mi) {
553       skipToNextVal();
554     }
555   
556   public:
557     typedef ValOpIterator<MITy, VTy> _Self;
558     
559     inline VTy operator*() const {
560       return MI->getOperand(i).getVRegValue();
561     }
562
563     const MachineOperand &getMachineOperand() const { return MI->getOperand(i);}
564           MachineOperand &getMachineOperand()       { return MI->getOperand(i);}
565
566     inline VTy operator->() const { return operator*(); }
567
568     inline bool isDef()       const { return MI->getOperand(i).opIsDef(); } 
569     inline bool isDefAndUse() const { return MI->getOperand(i).opIsDefAndUse();}
570
571     inline _Self& operator++() { i++; skipToNextVal(); return *this; }
572     inline _Self  operator++(int) { _Self tmp = *this; ++*this; return tmp; }
573
574     inline bool operator==(const _Self &y) const { 
575       return i == y.i;
576     }
577     inline bool operator!=(const _Self &y) const { 
578       return !operator==(y);
579     }
580
581     static _Self begin(MITy MI) {
582       return _Self(MI, 0);
583     }
584     static _Self end(MITy MI) {
585       return _Self(MI, MI->getNumOperands());
586     }
587   };
588
589   // define begin() and end()
590   val_op_iterator begin() { return val_op_iterator::begin(this); }
591   val_op_iterator end()   { return val_op_iterator::end(this); }
592
593   const_val_op_iterator begin() const {
594     return const_val_op_iterator::begin(this);
595   }
596   const_val_op_iterator end() const {
597     return const_val_op_iterator::end(this);
598   }
599 };
600
601
602 // Define here to enable inlining of the functions used.
603 // 
604 void MachineInstr::addImplicitRef(Value* V,
605                                   bool isDef,
606                                   bool isDefAndUse)
607 {
608   ++numImplicitRefs;
609   addRegOperand(V, isDef, isDefAndUse);
610 }
611
612 void MachineInstr::setImplicitRef(unsigned i,
613                                   Value* V,
614                                   bool isDef,
615                                   bool isDefAndUse)
616 {
617   assert(i < getNumImplicitRefs() && "setImplicitRef() out of range!");
618   SetMachineOperandVal(i + getNumOperands(),
619                        MachineOperand::MO_VirtualRegister,
620                        V, isDef, isDefAndUse);
621 }
622
623
624 //---------------------------------------------------------------------------
625 // Debugging Support
626 //---------------------------------------------------------------------------
627
628 std::ostream& operator<<        (std::ostream& os,
629                                  const MachineInstr& minstr);
630
631 std::ostream& operator<<        (std::ostream& os,
632                                  const MachineOperand& mop);
633                                          
634 void PrintMachineInstructions   (const Function *F);
635
636 #endif