LiveInterval.h revision 52c1afcaea61440950a11a4ccadac4354420d727
1//===-- llvm/CodeGen/LiveInterval.h - Interval representation ---*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the LiveRange and LiveInterval classes. Given some 11// numbering of each the machine instructions an interval [i, j) is said to be a 12// live interval for register v if there is no instruction with number j' >= j 13// such that v is live at j' and there is no instruction with number i' < i such 14// that v is live at i'. In this implementation intervals can have holes, 15// i.e. an interval might look like [1,20), [50,65), [1000,1001). Each 16// individual range is represented as an instance of LiveRange, and the whole 17// interval is represented as an instance of LiveInterval. 18// 19//===----------------------------------------------------------------------===// 20 21#ifndef LLVM_CODEGEN_LIVEINTERVAL_H 22#define LLVM_CODEGEN_LIVEINTERVAL_H 23 24#include "llvm/ADT/SmallVector.h" 25#include "llvm/Support/Allocator.h" 26#include "llvm/Support/AlignOf.h" 27#include <iosfwd> 28#include <cassert> 29#include <climits> 30 31namespace llvm { 32 class MachineInstr; 33 class MachineRegisterInfo; 34 class TargetRegisterInfo; 35 class raw_ostream; 36 37 /// VNInfo - Value Number Information. 38 /// This class holds information about a machine level values, including 39 /// definition and use points. 40 /// 41 /// Care must be taken in interpreting the def index of the value. The 42 /// following rules apply: 43 /// 44 /// If the isDefAccurate() method returns false then def does not contain the 45 /// index of the defining MachineInstr, or even (necessarily) to a 46 /// MachineInstr at all. In general such a def index is not meaningful 47 /// and should not be used. The exception is that, for values originally 48 /// defined by PHI instructions, after PHI elimination def will contain the 49 /// index of the MBB in which the PHI originally existed. This can be used 50 /// to insert code (spills or copies) which deals with the value, which will 51 /// be live in to the block. 52 53 class VNInfo { 54 private: 55 enum { 56 HAS_PHI_KILL = 1, 57 REDEF_BY_EC = 1 << 1, 58 IS_PHI_DEF = 1 << 2, 59 IS_UNUSED = 1 << 3, 60 IS_DEF_ACCURATE = 1 << 4 61 }; 62 63 unsigned char flags; 64 union { 65 MachineInstr *copy; 66 unsigned reg; 67 } cr; 68 69 public: 70 /// Holds information about individual kills. 71 struct KillInfo { 72 bool isPHIKill : 1; 73 unsigned killIdx : 31; 74 75 KillInfo(bool isPHIKill, unsigned killIdx) 76 : isPHIKill(isPHIKill), killIdx(killIdx) { 77 78 assert(killIdx != 0 && "Zero kill indices are no longer permitted."); 79 } 80 81 }; 82 83 typedef SmallVector<KillInfo, 4> KillSet; 84 85 /// The ID number of this value. 86 unsigned id; 87 88 /// The index of the defining instruction (if isDefAccurate() returns true). 89 unsigned def; 90 91 KillSet kills; 92 93 VNInfo() 94 : flags(IS_UNUSED), id(~1U), def(0) { cr.copy = 0; } 95 96 /// VNInfo constructor. 97 /// d is presumed to point to the actual defining instr. If it doesn't 98 /// setIsDefAccurate(false) should be called after construction. 99 VNInfo(unsigned i, unsigned d, MachineInstr *c) 100 : flags(IS_DEF_ACCURATE), id(i), def(d) { cr.copy = c; } 101 102 /// VNInfo construtor, copies values from orig, except for the value number. 103 VNInfo(unsigned i, const VNInfo &orig) 104 : flags(orig.flags), cr(orig.cr), id(i), def(orig.def), kills(orig.kills) 105 { } 106 107 /// Copy from the parameter into this VNInfo. 108 void copyFrom(VNInfo &src) { 109 flags = src.flags; 110 cr = src.cr; 111 def = src.def; 112 kills = src.kills; 113 } 114 115 /// Used for copying value number info. 116 unsigned getFlags() const { return flags; } 117 void setFlags(unsigned flags) { this->flags = flags; } 118 119 /// For a register interval, if this VN was definied by a copy instr 120 /// getCopy() returns a pointer to it, otherwise returns 0. 121 /// For a stack interval the behaviour of this method is undefined. 122 MachineInstr* getCopy() const { return cr.copy; } 123 /// For a register interval, set the copy member. 124 /// This method should not be called on stack intervals as it may lead to 125 /// undefined behavior. 126 void setCopy(MachineInstr *c) { cr.copy = c; } 127 128 /// For a stack interval, returns the reg which this stack interval was 129 /// defined from. 130 /// For a register interval the behaviour of this method is undefined. 131 unsigned getReg() const { return cr.reg; } 132 /// For a stack interval, set the defining register. 133 /// This method should not be called on register intervals as it may lead 134 /// to undefined behaviour. 135 void setReg(unsigned reg) { cr.reg = reg; } 136 137 /// Returns true if one or more kills are PHI nodes. 138 bool hasPHIKill() const { return flags & HAS_PHI_KILL; } 139 void setHasPHIKill(bool hasKill) { 140 if (hasKill) 141 flags |= HAS_PHI_KILL; 142 else 143 flags &= ~HAS_PHI_KILL; 144 } 145 146 /// Returns true if this value is re-defined by an early clobber somewhere 147 /// during the live range. 148 bool hasRedefByEC() const { return flags & REDEF_BY_EC; } 149 void setHasRedefByEC(bool hasRedef) { 150 if (hasRedef) 151 flags |= REDEF_BY_EC; 152 else 153 flags &= ~REDEF_BY_EC; 154 } 155 156 /// Returns true if this value is defined by a PHI instruction (or was, 157 /// PHI instrucions may have been eliminated). 158 bool isPHIDef() const { return flags & IS_PHI_DEF; } 159 void setIsPHIDef(bool phiDef) { 160 if (phiDef) 161 flags |= IS_PHI_DEF; 162 else 163 flags &= ~IS_PHI_DEF; 164 } 165 166 /// Returns true if this value is unused. 167 bool isUnused() const { return flags & IS_UNUSED; } 168 void setIsUnused(bool unused) { 169 if (unused) 170 flags |= IS_UNUSED; 171 else 172 flags &= ~IS_UNUSED; 173 } 174 175 /// Returns true if the def is accurate. 176 bool isDefAccurate() const { return flags & IS_DEF_ACCURATE; } 177 void setIsDefAccurate(bool defAccurate) { 178 if (defAccurate) 179 flags |= IS_DEF_ACCURATE; 180 else 181 flags &= ~IS_DEF_ACCURATE; 182 } 183 184 }; 185 186 inline bool operator<(const VNInfo::KillInfo &k1, const VNInfo::KillInfo &k2) { 187 return k1.killIdx < k2.killIdx; 188 } 189 190 inline bool operator<(const VNInfo::KillInfo &k, unsigned idx) { 191 return k.killIdx < idx; 192 } 193 194 inline bool operator<(unsigned idx, const VNInfo::KillInfo &k) { 195 return idx < k.killIdx; 196 } 197 198 /// LiveRange structure - This represents a simple register range in the 199 /// program, with an inclusive start point and an exclusive end point. 200 /// These ranges are rendered as [start,end). 201 struct LiveRange { 202 unsigned start; // Start point of the interval (inclusive) 203 unsigned end; // End point of the interval (exclusive) 204 VNInfo *valno; // identifier for the value contained in this interval. 205 206 LiveRange(unsigned S, unsigned E, VNInfo *V) : start(S), end(E), valno(V) { 207 assert(S < E && "Cannot create empty or backwards range"); 208 } 209 210 /// contains - Return true if the index is covered by this range. 211 /// 212 bool contains(unsigned I) const { 213 return start <= I && I < end; 214 } 215 216 bool operator<(const LiveRange &LR) const { 217 return start < LR.start || (start == LR.start && end < LR.end); 218 } 219 bool operator==(const LiveRange &LR) const { 220 return start == LR.start && end == LR.end; 221 } 222 223 void dump() const; 224 void print(std::ostream &os) const; 225 void print(std::ostream *os) const { if (os) print(*os); } 226 void print(raw_ostream &os) const; 227 void print(raw_ostream *os) const { if (os) print(*os); } 228 229 private: 230 LiveRange(); // DO NOT IMPLEMENT 231 }; 232 233 std::ostream& operator<<(std::ostream& os, const LiveRange &LR); 234 raw_ostream& operator<<(raw_ostream& os, const LiveRange &LR); 235 236 237 inline bool operator<(unsigned V, const LiveRange &LR) { 238 return V < LR.start; 239 } 240 241 inline bool operator<(const LiveRange &LR, unsigned V) { 242 return LR.start < V; 243 } 244 245 /// LiveInterval - This class represents some number of live ranges for a 246 /// register or value. This class also contains a bit of register allocator 247 /// state. 248 class LiveInterval { 249 public: 250 251 typedef SmallVector<LiveRange,4> Ranges; 252 typedef SmallVector<VNInfo*,4> VNInfoList; 253 254 unsigned reg; // the register or stack slot of this interval 255 // if the top bits is set, it represents a stack slot. 256 float weight; // weight of this interval 257 Ranges ranges; // the ranges in which this register is live 258 VNInfoList valnos; // value#'s 259 260 struct InstrSlots { 261 enum { 262 LOAD = 0, 263 USE = 1, 264 DEF = 2, 265 STORE = 3, 266 NUM = 4 267 }; 268 269 static unsigned scale(unsigned slot, unsigned factor) { 270 unsigned index = slot / NUM, 271 offset = slot % NUM; 272 assert(index <= ~0U / (factor * NUM) && 273 "Rescaled interval would overflow"); 274 return index * NUM * factor + offset; 275 } 276 277 }; 278 279 LiveInterval(unsigned Reg, float Weight, bool IsSS = false) 280 : reg(Reg), weight(Weight) { 281 if (IsSS) 282 reg = reg | (1U << (sizeof(unsigned)*CHAR_BIT-1)); 283 } 284 285 typedef Ranges::iterator iterator; 286 iterator begin() { return ranges.begin(); } 287 iterator end() { return ranges.end(); } 288 289 typedef Ranges::const_iterator const_iterator; 290 const_iterator begin() const { return ranges.begin(); } 291 const_iterator end() const { return ranges.end(); } 292 293 typedef VNInfoList::iterator vni_iterator; 294 vni_iterator vni_begin() { return valnos.begin(); } 295 vni_iterator vni_end() { return valnos.end(); } 296 297 typedef VNInfoList::const_iterator const_vni_iterator; 298 const_vni_iterator vni_begin() const { return valnos.begin(); } 299 const_vni_iterator vni_end() const { return valnos.end(); } 300 301 /// advanceTo - Advance the specified iterator to point to the LiveRange 302 /// containing the specified position, or end() if the position is past the 303 /// end of the interval. If no LiveRange contains this position, but the 304 /// position is in a hole, this method returns an iterator pointing the the 305 /// LiveRange immediately after the hole. 306 iterator advanceTo(iterator I, unsigned Pos) { 307 if (Pos >= endNumber()) 308 return end(); 309 while (I->end <= Pos) ++I; 310 return I; 311 } 312 313 void clear() { 314 while (!valnos.empty()) { 315 VNInfo *VNI = valnos.back(); 316 valnos.pop_back(); 317 VNI->~VNInfo(); 318 } 319 320 ranges.clear(); 321 } 322 323 /// isStackSlot - Return true if this is a stack slot interval. 324 /// 325 bool isStackSlot() const { 326 return reg & (1U << (sizeof(unsigned)*CHAR_BIT-1)); 327 } 328 329 /// getStackSlotIndex - Return stack slot index if this is a stack slot 330 /// interval. 331 int getStackSlotIndex() const { 332 assert(isStackSlot() && "Interval is not a stack slot interval!"); 333 return reg & ~(1U << (sizeof(unsigned)*CHAR_BIT-1)); 334 } 335 336 bool hasAtLeastOneValue() const { return !valnos.empty(); } 337 338 bool containsOneValue() const { return valnos.size() == 1; } 339 340 unsigned getNumValNums() const { return (unsigned)valnos.size(); } 341 342 /// getValNumInfo - Returns pointer to the specified val#. 343 /// 344 inline VNInfo *getValNumInfo(unsigned ValNo) { 345 return valnos[ValNo]; 346 } 347 inline const VNInfo *getValNumInfo(unsigned ValNo) const { 348 return valnos[ValNo]; 349 } 350 351 /// getNextValue - Create a new value number and return it. MIIdx specifies 352 /// the instruction that defines the value number. 353 VNInfo *getNextValue(unsigned MIIdx, MachineInstr *CopyMI, 354 bool isDefAccurate, BumpPtrAllocator &VNInfoAllocator) { 355 356 assert(MIIdx != ~0u && MIIdx != ~1u && 357 "PHI def / unused flags should now be passed explicitly."); 358 VNInfo *VNI = 359 static_cast<VNInfo*>(VNInfoAllocator.Allocate((unsigned)sizeof(VNInfo), 360 alignof<VNInfo>())); 361 new (VNI) VNInfo((unsigned)valnos.size(), MIIdx, CopyMI); 362 VNI->setIsDefAccurate(isDefAccurate); 363 valnos.push_back(VNI); 364 return VNI; 365 } 366 367 /// Create a copy of the given value. The new value will be identical except 368 /// for the Value number. 369 VNInfo *createValueCopy(const VNInfo *orig, BumpPtrAllocator &VNInfoAllocator) { 370 371 VNInfo *VNI = 372 static_cast<VNInfo*>(VNInfoAllocator.Allocate((unsigned)sizeof(VNInfo), 373 alignof<VNInfo>())); 374 375 new (VNI) VNInfo((unsigned)valnos.size(), *orig); 376 valnos.push_back(VNI); 377 return VNI; 378 } 379 380 /// addKill - Add a kill instruction index to the specified value 381 /// number. 382 static void addKill(VNInfo *VNI, unsigned KillIdx, bool phiKill) { 383 VNInfo::KillSet &kills = VNI->kills; 384 VNInfo::KillInfo newKill(phiKill, KillIdx); 385 if (kills.empty()) { 386 kills.push_back(newKill); 387 } else { 388 VNInfo::KillSet::iterator 389 I = std::lower_bound(kills.begin(), kills.end(), newKill); 390 kills.insert(I, newKill); 391 } 392 } 393 394 /// addKills - Add a number of kills into the VNInfo kill vector. If this 395 /// interval is live at a kill point, then the kill is not added. 396 void addKills(VNInfo *VNI, const VNInfo::KillSet &kills) { 397 for (unsigned i = 0, e = static_cast<unsigned>(kills.size()); 398 i != e; ++i) { 399 const VNInfo::KillInfo &Kill = kills[i]; 400 if (!liveBeforeAndAt(Kill.killIdx)) { 401 VNInfo::KillSet::iterator 402 I = std::lower_bound(VNI->kills.begin(), VNI->kills.end(), Kill); 403 VNI->kills.insert(I, Kill); 404 } 405 } 406 } 407 408 /// removeKill - Remove the specified kill from the list of kills of 409 /// the specified val#. 410 static bool removeKill(VNInfo *VNI, unsigned KillIdx) { 411 VNInfo::KillSet &kills = VNI->kills; 412 VNInfo::KillSet::iterator 413 I = std::lower_bound(kills.begin(), kills.end(), KillIdx); 414 if (I != kills.end() && I->killIdx == KillIdx) { 415 kills.erase(I); 416 return true; 417 } 418 return false; 419 } 420 421 /// removeKills - Remove all the kills in specified range 422 /// [Start, End] of the specified val#. 423 static void removeKills(VNInfo *VNI, unsigned Start, unsigned End) { 424 VNInfo::KillSet &kills = VNI->kills; 425 426 VNInfo::KillSet::iterator 427 I = std::lower_bound(kills.begin(), kills.end(), Start); 428 VNInfo::KillSet::iterator 429 E = std::upper_bound(kills.begin(), kills.end(), End); 430 kills.erase(I, E); 431 } 432 433 /// isKill - Return true if the specified index is a kill of the 434 /// specified val#. 435 static bool isKill(const VNInfo *VNI, unsigned KillIdx) { 436 const VNInfo::KillSet &kills = VNI->kills; 437 VNInfo::KillSet::const_iterator 438 I = std::lower_bound(kills.begin(), kills.end(), KillIdx); 439 return I != kills.end() && I->killIdx == KillIdx; 440 } 441 442 /// isOnlyLROfValNo - Return true if the specified live range is the only 443 /// one defined by the its val#. 444 bool isOnlyLROfValNo(const LiveRange *LR) { 445 for (const_iterator I = begin(), E = end(); I != E; ++I) { 446 const LiveRange *Tmp = I; 447 if (Tmp != LR && Tmp->valno == LR->valno) 448 return false; 449 } 450 return true; 451 } 452 453 /// MergeValueNumberInto - This method is called when two value nubmers 454 /// are found to be equivalent. This eliminates V1, replacing all 455 /// LiveRanges with the V1 value number with the V2 value number. This can 456 /// cause merging of V1/V2 values numbers and compaction of the value space. 457 VNInfo* MergeValueNumberInto(VNInfo *V1, VNInfo *V2); 458 459 /// MergeInClobberRanges - For any live ranges that are not defined in the 460 /// current interval, but are defined in the Clobbers interval, mark them 461 /// used with an unknown definition value. Caller must pass in reference to 462 /// VNInfoAllocator since it will create a new val#. 463 void MergeInClobberRanges(const LiveInterval &Clobbers, 464 BumpPtrAllocator &VNInfoAllocator); 465 466 /// MergeInClobberRange - Same as MergeInClobberRanges except it merge in a 467 /// single LiveRange only. 468 void MergeInClobberRange(unsigned Start, unsigned End, 469 BumpPtrAllocator &VNInfoAllocator); 470 471 /// MergeValueInAsValue - Merge all of the live ranges of a specific val# 472 /// in RHS into this live interval as the specified value number. 473 /// The LiveRanges in RHS are allowed to overlap with LiveRanges in the 474 /// current interval, it will replace the value numbers of the overlaped 475 /// live ranges with the specified value number. 476 void MergeRangesInAsValue(const LiveInterval &RHS, VNInfo *LHSValNo); 477 478 /// MergeValueInAsValue - Merge all of the live ranges of a specific val# 479 /// in RHS into this live interval as the specified value number. 480 /// The LiveRanges in RHS are allowed to overlap with LiveRanges in the 481 /// current interval, but only if the overlapping LiveRanges have the 482 /// specified value number. 483 void MergeValueInAsValue(const LiveInterval &RHS, 484 const VNInfo *RHSValNo, VNInfo *LHSValNo); 485 486 /// Copy - Copy the specified live interval. This copies all the fields 487 /// except for the register of the interval. 488 void Copy(const LiveInterval &RHS, MachineRegisterInfo *MRI, 489 BumpPtrAllocator &VNInfoAllocator); 490 491 bool empty() const { return ranges.empty(); } 492 493 /// beginNumber - Return the lowest numbered slot covered by interval. 494 unsigned beginNumber() const { 495 if (empty()) 496 return 0; 497 return ranges.front().start; 498 } 499 500 /// endNumber - return the maximum point of the interval of the whole, 501 /// exclusive. 502 unsigned endNumber() const { 503 if (empty()) 504 return 0; 505 return ranges.back().end; 506 } 507 508 bool expiredAt(unsigned index) const { 509 return index >= endNumber(); 510 } 511 512 bool liveAt(unsigned index) const; 513 514 // liveBeforeAndAt - Check if the interval is live at the index and the 515 // index just before it. If index is liveAt, check if it starts a new live 516 // range.If it does, then check if the previous live range ends at index-1. 517 bool liveBeforeAndAt(unsigned index) const; 518 519 /// getLiveRangeContaining - Return the live range that contains the 520 /// specified index, or null if there is none. 521 const LiveRange *getLiveRangeContaining(unsigned Idx) const { 522 const_iterator I = FindLiveRangeContaining(Idx); 523 return I == end() ? 0 : &*I; 524 } 525 526 /// getLiveRangeContaining - Return the live range that contains the 527 /// specified index, or null if there is none. 528 LiveRange *getLiveRangeContaining(unsigned Idx) { 529 iterator I = FindLiveRangeContaining(Idx); 530 return I == end() ? 0 : &*I; 531 } 532 533 /// FindLiveRangeContaining - Return an iterator to the live range that 534 /// contains the specified index, or end() if there is none. 535 const_iterator FindLiveRangeContaining(unsigned Idx) const; 536 537 /// FindLiveRangeContaining - Return an iterator to the live range that 538 /// contains the specified index, or end() if there is none. 539 iterator FindLiveRangeContaining(unsigned Idx); 540 541 /// findDefinedVNInfo - Find the VNInfo that's defined at the specified 542 /// index (register interval) or defined by the specified register (stack 543 /// inteval). 544 VNInfo *findDefinedVNInfo(unsigned DefIdxOrReg) const; 545 546 /// overlaps - Return true if the intersection of the two live intervals is 547 /// not empty. 548 bool overlaps(const LiveInterval& other) const { 549 return overlapsFrom(other, other.begin()); 550 } 551 552 /// overlaps - Return true if the live interval overlaps a range specified 553 /// by [Start, End). 554 bool overlaps(unsigned Start, unsigned End) const; 555 556 /// overlapsFrom - Return true if the intersection of the two live intervals 557 /// is not empty. The specified iterator is a hint that we can begin 558 /// scanning the Other interval starting at I. 559 bool overlapsFrom(const LiveInterval& other, const_iterator I) const; 560 561 /// addRange - Add the specified LiveRange to this interval, merging 562 /// intervals as appropriate. This returns an iterator to the inserted live 563 /// range (which may have grown since it was inserted. 564 void addRange(LiveRange LR) { 565 addRangeFrom(LR, ranges.begin()); 566 } 567 568 /// join - Join two live intervals (this, and other) together. This applies 569 /// mappings to the value numbers in the LHS/RHS intervals as specified. If 570 /// the intervals are not joinable, this aborts. 571 void join(LiveInterval &Other, const int *ValNoAssignments, 572 const int *RHSValNoAssignments, 573 SmallVector<VNInfo*, 16> &NewVNInfo, 574 MachineRegisterInfo *MRI); 575 576 /// isInOneLiveRange - Return true if the range specified is entirely in the 577 /// a single LiveRange of the live interval. 578 bool isInOneLiveRange(unsigned Start, unsigned End); 579 580 /// removeRange - Remove the specified range from this interval. Note that 581 /// the range must be a single LiveRange in its entirety. 582 void removeRange(unsigned Start, unsigned End, bool RemoveDeadValNo = false); 583 584 void removeRange(LiveRange LR, bool RemoveDeadValNo = false) { 585 removeRange(LR.start, LR.end, RemoveDeadValNo); 586 } 587 588 /// removeValNo - Remove all the ranges defined by the specified value#. 589 /// Also remove the value# from value# list. 590 void removeValNo(VNInfo *ValNo); 591 592 /// scaleNumbering - Renumber VNI and ranges to provide gaps for new 593 /// instructions. 594 void scaleNumbering(unsigned factor); 595 596 /// getSize - Returns the sum of sizes of all the LiveRange's. 597 /// 598 unsigned getSize() const; 599 600 /// ComputeJoinedWeight - Set the weight of a live interval after 601 /// Other has been merged into it. 602 void ComputeJoinedWeight(const LiveInterval &Other); 603 604 bool operator<(const LiveInterval& other) const { 605 return beginNumber() < other.beginNumber(); 606 } 607 608 void print(std::ostream &OS, const TargetRegisterInfo *TRI = 0) const; 609 void print(std::ostream *OS, const TargetRegisterInfo *TRI = 0) const { 610 if (OS) print(*OS, TRI); 611 } 612 void print(raw_ostream &OS, const TargetRegisterInfo *TRI = 0) const; 613 void print(raw_ostream *OS, const TargetRegisterInfo *TRI = 0) const { 614 if (OS) print(*OS, TRI); 615 } 616 void dump() const; 617 618 private: 619 620 Ranges::iterator addRangeFrom(LiveRange LR, Ranges::iterator From); 621 void extendIntervalEndTo(Ranges::iterator I, unsigned NewEnd); 622 Ranges::iterator extendIntervalStartTo(Ranges::iterator I, unsigned NewStr); 623 LiveInterval& operator=(const LiveInterval& rhs); // DO NOT IMPLEMENT 624 625 }; 626 627 inline std::ostream &operator<<(std::ostream &OS, const LiveInterval &LI) { 628 LI.print(OS); 629 return OS; 630 } 631 inline raw_ostream &operator<<(raw_ostream &OS, const LiveInterval &LI) { 632 LI.print(OS); 633 return OS; 634 } 635} 636 637#endif 638