diff options
author | Derek Schuff <dschuff@chromium.org> | 2012-09-19 16:10:54 -0700 |
---|---|---|
committer | Derek Schuff <dschuff@chromium.org> | 2012-09-19 16:10:54 -0700 |
commit | 0e15ffd8cb1ec642eddb96380660914ff2b007e1 (patch) | |
tree | bc5ccf8c89bfe799bb276625e8e0bd6d84e3a75c /include/llvm | |
parent | 5e79ec1d7ada2e14283e2b69b6f4375eb4dd1890 (diff) | |
parent | 020aba0c3b6092e353e133446cb6453f95f0d61b (diff) |
Merge commit '020aba0c3b6092e353e133446cb6453f95f0d61b'
Diffstat (limited to 'include/llvm')
-rw-r--r-- | include/llvm/ADT/FoldingSet.h | 9 | ||||
-rw-r--r-- | include/llvm/CodeGen/MachineScheduler.h | 218 | ||||
-rw-r--r-- | include/llvm/InitializePasses.h | 1 | ||||
-rw-r--r-- | include/llvm/LinkAllPasses.h | 1 | ||||
-rw-r--r-- | include/llvm/MC/MCAsmInfo.h | 20 | ||||
-rw-r--r-- | include/llvm/MC/MCRegisterInfo.h | 7 | ||||
-rw-r--r-- | include/llvm/Object/ELF.h | 47 | ||||
-rw-r--r-- | include/llvm/TableGen/Record.h | 2 | ||||
-rw-r--r-- | include/llvm/Target/TargetRegisterInfo.h | 34 | ||||
-rw-r--r-- | include/llvm/Transforms/IPO.h | 5 | ||||
-rw-r--r-- | include/llvm/Transforms/Utils/BypassSlowDivision.h | 10 | ||||
-rw-r--r-- | include/llvm/Transforms/Utils/SimplifyIndVar.h | 2 |
12 files changed, 316 insertions, 40 deletions
diff --git a/include/llvm/ADT/FoldingSet.h b/include/llvm/ADT/FoldingSet.h index ba415ac2d6..375d84abeb 100644 --- a/include/llvm/ADT/FoldingSet.h +++ b/include/llvm/ADT/FoldingSet.h @@ -278,6 +278,10 @@ public: bool operator==(FoldingSetNodeIDRef) const; + /// Used to compare the "ordering" of two nodes as defined by the + /// profiled bits and their ordering defined by memcmp(). + bool operator<(FoldingSetNodeIDRef) const; + const unsigned *getData() const { return Data; } size_t getSize() const { return Size; } }; @@ -327,6 +331,11 @@ public: bool operator==(const FoldingSetNodeID &RHS) const; bool operator==(const FoldingSetNodeIDRef RHS) const; + /// Used to compare the "ordering" of two nodes as defined by the + /// profiled bits and their ordering defined by memcmp(). + bool operator<(const FoldingSetNodeID &RHS) const; + bool operator<(const FoldingSetNodeIDRef RHS) const; + /// Intern - Copy this node's data to a memory region allocated from the /// given allocator and return a FoldingSetNodeIDRef describing the /// interned data. diff --git a/include/llvm/CodeGen/MachineScheduler.h b/include/llvm/CodeGen/MachineScheduler.h index 8da2045ad0..f87c2a5c40 100644 --- a/include/llvm/CodeGen/MachineScheduler.h +++ b/include/llvm/CodeGen/MachineScheduler.h @@ -28,9 +28,16 @@ #define MACHINESCHEDULER_H #include "llvm/CodeGen/MachinePassRegistry.h" +#include "llvm/CodeGen/RegisterPressure.h" +#include "llvm/CodeGen/ScheduleDAGInstrs.h" +#include "llvm/Target/TargetInstrInfo.h" +#include "llvm/MC/MCInstrItineraries.h" namespace llvm { +extern cl::opt<bool> ForceTopDown; +extern cl::opt<bool> ForceBottomUp; + class AliasAnalysis; class LiveIntervals; class MachineDominatorTree; @@ -93,6 +100,217 @@ public: } }; +class ScheduleDAGMI; + +/// MachineSchedStrategy - Interface to the scheduling algorithm used by +/// ScheduleDAGMI. +class MachineSchedStrategy { +public: + virtual ~MachineSchedStrategy() {} + + /// Initialize the strategy after building the DAG for a new region. + virtual void initialize(ScheduleDAGMI *DAG) = 0; + + /// Pick the next node to schedule, or return NULL. Set IsTopNode to true to + /// schedule the node at the top of the unscheduled region. Otherwise it will + /// be scheduled at the bottom. + virtual SUnit *pickNode(bool &IsTopNode) = 0; + + /// Notify MachineSchedStrategy that ScheduleDAGMI has scheduled an + /// instruction and updated scheduled/remaining flags in the DAG nodes. + virtual void schedNode(SUnit *SU, bool IsTopNode) = 0; + + /// When all predecessor dependencies have been resolved, free this node for + /// top-down scheduling. + virtual void releaseTopNode(SUnit *SU) = 0; + /// When all successor dependencies have been resolved, free this node for + /// bottom-up scheduling. + virtual void releaseBottomNode(SUnit *SU) = 0; +}; + +/// ReadyQueue encapsulates vector of "ready" SUnits with basic convenience +/// methods for pushing and removing nodes. ReadyQueue's are uniquely identified +/// by an ID. SUnit::NodeQueueId is a mask of the ReadyQueues the SUnit is in. +/// +/// This is a convenience class that may be used by implementations of +/// MachineSchedStrategy. +class ReadyQueue { + unsigned ID; + std::string Name; + std::vector<SUnit*> Queue; + +public: + ReadyQueue(unsigned id, const Twine &name): ID(id), Name(name.str()) {} + + unsigned getID() const { return ID; } + + StringRef getName() const { return Name; } + + // SU is in this queue if it's NodeQueueID is a superset of this ID. + bool isInQueue(SUnit *SU) const { return (SU->NodeQueueId & ID); } + + bool empty() const { return Queue.empty(); } + + unsigned size() const { return Queue.size(); } + + typedef std::vector<SUnit*>::iterator iterator; + + iterator begin() { return Queue.begin(); } + + iterator end() { return Queue.end(); } + + iterator find(SUnit *SU) { + return std::find(Queue.begin(), Queue.end(), SU); + } + + void push(SUnit *SU) { + Queue.push_back(SU); + SU->NodeQueueId |= ID; + } + + void remove(iterator I) { + (*I)->NodeQueueId &= ~ID; + *I = Queue.back(); + Queue.pop_back(); + } + +#ifndef NDEBUG + void dump(); +#endif +}; + +/// ScheduleDAGMI is an implementation of ScheduleDAGInstrs that schedules +/// machine instructions while updating LiveIntervals and tracking regpressure. +class ScheduleDAGMI : public ScheduleDAGInstrs { +protected: + AliasAnalysis *AA; + RegisterClassInfo *RegClassInfo; + MachineSchedStrategy *SchedImpl; + + MachineBasicBlock::iterator LiveRegionEnd; + + /// Register pressure in this region computed by buildSchedGraph. + IntervalPressure RegPressure; + RegPressureTracker RPTracker; + + /// List of pressure sets that exceed the target's pressure limit before + /// scheduling, listed in increasing set ID order. Each pressure set is paired + /// with its max pressure in the currently scheduled regions. + std::vector<PressureElement> RegionCriticalPSets; + + /// The top of the unscheduled zone. + MachineBasicBlock::iterator CurrentTop; + IntervalPressure TopPressure; + RegPressureTracker TopRPTracker; + + /// The bottom of the unscheduled zone. + MachineBasicBlock::iterator CurrentBottom; + IntervalPressure BotPressure; + RegPressureTracker BotRPTracker; + +#ifndef NDEBUG + /// The number of instructions scheduled so far. Used to cut off the + /// scheduler at the point determined by misched-cutoff. + unsigned NumInstrsScheduled; +#endif + +public: + ScheduleDAGMI(MachineSchedContext *C, MachineSchedStrategy *S): + ScheduleDAGInstrs(*C->MF, *C->MLI, *C->MDT, /*IsPostRA=*/false, C->LIS), + AA(C->AA), RegClassInfo(C->RegClassInfo), SchedImpl(S), + RPTracker(RegPressure), CurrentTop(), TopRPTracker(TopPressure), + CurrentBottom(), BotRPTracker(BotPressure) { +#ifndef NDEBUG + NumInstrsScheduled = 0; +#endif + } + + virtual ~ScheduleDAGMI() { + delete SchedImpl; + } + + MachineBasicBlock::iterator top() const { return CurrentTop; } + MachineBasicBlock::iterator bottom() const { return CurrentBottom; } + + /// Implement the ScheduleDAGInstrs interface for handling the next scheduling + /// region. This covers all instructions in a block, while schedule() may only + /// cover a subset. + void enterRegion(MachineBasicBlock *bb, + MachineBasicBlock::iterator begin, + MachineBasicBlock::iterator end, + unsigned endcount); + + + /// Implement ScheduleDAGInstrs interface for scheduling a sequence of + /// reorderable instructions. + virtual void schedule(); + + /// Get current register pressure for the top scheduled instructions. + const IntervalPressure &getTopPressure() const { return TopPressure; } + const RegPressureTracker &getTopRPTracker() const { return TopRPTracker; } + + /// Get current register pressure for the bottom scheduled instructions. + const IntervalPressure &getBotPressure() const { return BotPressure; } + const RegPressureTracker &getBotRPTracker() const { return BotRPTracker; } + + /// Get register pressure for the entire scheduling region before scheduling. + const IntervalPressure &getRegPressure() const { return RegPressure; } + + const std::vector<PressureElement> &getRegionCriticalPSets() const { + return RegionCriticalPSets; + } + + /// getIssueWidth - Return the max instructions per scheduling group. + unsigned getIssueWidth() const { + return (InstrItins && InstrItins->SchedModel) + ? InstrItins->SchedModel->IssueWidth : 1; + } + + /// getNumMicroOps - Return the number of issue slots required for this MI. + unsigned getNumMicroOps(MachineInstr *MI) const { + if (!InstrItins) return 1; + int UOps = InstrItins->getNumMicroOps(MI->getDesc().getSchedClass()); + return (UOps >= 0) ? UOps : TII->getNumMicroOps(InstrItins, MI); + } + +protected: + // Top-Level entry points for the schedule() driver... + + /// Call ScheduleDAGInstrs::buildSchedGraph with register pressure tracking + /// enabled. This sets up three trackers. RPTracker will cover the entire DAG + /// region, TopTracker and BottomTracker will be initialized to the top and + /// bottom of the DAG region without covereing any unscheduled instruction. + void buildDAGWithRegPressure(); + + /// Identify DAG roots and setup scheduler queues. + void initQueues(); + + /// Move an instruction and update register pressure. + void scheduleMI(SUnit *SU, bool IsTopNode); + + /// Update scheduler DAG and queues after scheduling an instruction. + void updateQueues(SUnit *SU, bool IsTopNode); + + /// Reinsert debug_values recorded in ScheduleDAGInstrs::DbgValues. + void placeDebugValues(); + + // Lesser helpers... + + void initRegPressure(); + + void updateScheduledPressure(std::vector<unsigned> NewMaxPressure); + + void moveInstruction(MachineInstr *MI, MachineBasicBlock::iterator InsertPos); + bool checkSchedLimit(); + + void releaseRoots(); + + void releaseSucc(SUnit *SU, SDep *SuccEdge); + void releaseSuccessors(SUnit *SU); + void releasePred(SUnit *SU, SDep *PredEdge); + void releasePredecessors(SUnit *SU); +}; + } // namespace llvm #endif diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index ccf906be08..b3cd79b8cc 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -178,6 +178,7 @@ void initializeMachineVerifierPassPass(PassRegistry&); void initializeMemCpyOptPass(PassRegistry&); void initializeMemDepPrinterPass(PassRegistry&); void initializeMemoryDependenceAnalysisPass(PassRegistry&); +void initializeMetaRenamerPass(PassRegistry&); void initializeMergeFunctionsPass(PassRegistry&); void initializeModuleDebugInfoPrinterPass(PassRegistry&); void initializeNoAAPass(PassRegistry&); diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index fe4c92a295..c01e471024 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -141,6 +141,7 @@ namespace { (void) llvm::createLoopDeletionPass(); (void) llvm::createPostDomTree(); (void) llvm::createInstructionNamerPass(); + (void) llvm::createMetaRenamerPass(); (void) llvm::createFunctionAttrsPass(); (void) llvm::createMergeFunctionsPass(); (void) llvm::createPrintModulePass(0); diff --git a/include/llvm/MC/MCAsmInfo.h b/include/llvm/MC/MCAsmInfo.h index 03da47c2ce..29ec1020c3 100644 --- a/include/llvm/MC/MCAsmInfo.h +++ b/include/llvm/MC/MCAsmInfo.h @@ -33,7 +33,7 @@ namespace llvm { } namespace LCOMM { - enum LCOMMType { None, NoAlignment, ByteAlignment }; + enum LCOMMType { NoAlignment, ByteAlignment, Log2Alignment }; } /// MCAsmInfo - This class is intended to be used as a base class for asm @@ -255,14 +255,14 @@ namespace llvm { /// .long a - b bool HasAggressiveSymbolFolding; // Defaults to true. - /// LCOMMDirectiveType - Describes if the target supports the .lcomm - /// directive and whether it has an alignment parameter. - LCOMM::LCOMMType LCOMMDirectiveType; // Defaults to LCOMM::None. - - /// COMMDirectiveAlignmentIsInBytes - True is COMMDirective's optional + /// COMMDirectiveAlignmentIsInBytes - True is .comm's and .lcomms optional /// alignment is to be specified in bytes instead of log2(n). bool COMMDirectiveAlignmentIsInBytes; // Defaults to true; + /// LCOMMDirectiveAlignment - Describes if the .lcomm directive for the + /// target supports an alignment argument and how it is interpreted. + LCOMM::LCOMMType LCOMMDirectiveAlignmentType; // Defaults to NoAlignment. + /// HasDotTypeDotSizeDirective - True if the target has .type and .size /// directives, this is true for most ELF targets. bool HasDotTypeDotSizeDirective; // Defaults to true. @@ -511,13 +511,13 @@ namespace llvm { bool hasAggressiveSymbolFolding() const { return HasAggressiveSymbolFolding; } - LCOMM::LCOMMType getLCOMMDirectiveType() const { - return LCOMMDirectiveType; - } - bool hasDotTypeDotSizeDirective() const {return HasDotTypeDotSizeDirective;} bool getCOMMDirectiveAlignmentIsInBytes() const { return COMMDirectiveAlignmentIsInBytes; } + LCOMM::LCOMMType getLCOMMDirectiveAlignmentType() const { + return LCOMMDirectiveAlignmentType; + } + bool hasDotTypeDotSizeDirective() const {return HasDotTypeDotSizeDirective;} bool hasSingleParameterDotFile() const { return HasSingleParameterDotFile; } bool hasNoDeadStrip() const { return HasNoDeadStrip; } bool hasSymbolResolver() const { return HasSymbolResolver; } diff --git a/include/llvm/MC/MCRegisterInfo.h b/include/llvm/MC/MCRegisterInfo.h index 46a9d71fff..6749bdffc2 100644 --- a/include/llvm/MC/MCRegisterInfo.h +++ b/include/llvm/MC/MCRegisterInfo.h @@ -333,6 +333,13 @@ public: return NumRegs; } + /// getNumSubRegIndices - Return the number of sub-register indices + /// understood by the target. Index 0 is reserved for the no-op sub-register, + /// while 1 to getNumSubRegIndices() - 1 represent real sub-registers. + unsigned getNumSubRegIndices() const { + return NumSubRegIndices; + } + /// getNumRegUnits - Return the number of (native) register units in the /// target. Register units are numbered from 0 to getNumRegUnits() - 1. They /// can be accessed through MCRegUnitIterator defined below. diff --git a/include/llvm/Object/ELF.h b/include/llvm/Object/ELF.h index d672e96f5c..06edf432f5 100644 --- a/include/llvm/Object/ELF.h +++ b/include/llvm/Object/ELF.h @@ -387,11 +387,36 @@ struct Elf_Rel_Impl<target_endianness, false, isRela> } }; +template<support::endianness target_endianness, bool is64Bits> +struct Elf_Ehdr_Impl { + LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) + unsigned char e_ident[ELF::EI_NIDENT]; // ELF Identification bytes + Elf_Half e_type; // Type of file (see ET_*) + Elf_Half e_machine; // Required architecture for this file (see EM_*) + Elf_Word e_version; // Must be equal to 1 + Elf_Addr e_entry; // Address to jump to in order to start program + Elf_Off e_phoff; // Program header table's file offset, in bytes + Elf_Off e_shoff; // Section header table's file offset, in bytes + Elf_Word e_flags; // Processor-specific flags + Elf_Half e_ehsize; // Size of ELF header, in bytes + Elf_Half e_phentsize;// Size of an entry in the program header table + Elf_Half e_phnum; // Number of entries in the program header table + Elf_Half e_shentsize;// Size of an entry in the section header table + Elf_Half e_shnum; // Number of entries in the section header table + Elf_Half e_shstrndx; // Section header table index of section name + // string table + bool checkMagic() const { + return (memcmp(e_ident, ELF::ElfMagic, strlen(ELF::ElfMagic))) == 0; + } + unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; } + unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; } +}; template<support::endianness target_endianness, bool is64Bits> class ELFObjectFile : public ObjectFile { LLVM_ELF_IMPORT_TYPES(target_endianness, is64Bits) + typedef Elf_Ehdr_Impl<target_endianness, is64Bits> Elf_Ehdr; typedef Elf_Shdr_Impl<target_endianness, is64Bits> Elf_Shdr; typedef Elf_Sym_Impl<target_endianness, is64Bits> Elf_Sym; typedef Elf_Dyn_Impl<target_endianness, is64Bits> Elf_Dyn; @@ -406,28 +431,6 @@ class ELFObjectFile : public ObjectFile { typedef content_iterator<DynRef> dyn_iterator; protected: - struct Elf_Ehdr { - unsigned char e_ident[ELF::EI_NIDENT]; // ELF Identification bytes - Elf_Half e_type; // Type of file (see ET_*) - Elf_Half e_machine; // Required architecture for this file (see EM_*) - Elf_Word e_version; // Must be equal to 1 - Elf_Addr e_entry; // Address to jump to in order to start program - Elf_Off e_phoff; // Program header table's file offset, in bytes - Elf_Off e_shoff; // Section header table's file offset, in bytes - Elf_Word e_flags; // Processor-specific flags - Elf_Half e_ehsize; // Size of ELF header, in bytes - Elf_Half e_phentsize;// Size of an entry in the program header table - Elf_Half e_phnum; // Number of entries in the program header table - Elf_Half e_shentsize;// Size of an entry in the section header table - Elf_Half e_shnum; // Number of entries in the section header table - Elf_Half e_shstrndx; // Section header table index of section name - // string table - bool checkMagic() const { - return (memcmp(e_ident, ELF::ElfMagic, strlen(ELF::ElfMagic))) == 0; - } - unsigned char getFileClass() const { return e_ident[ELF::EI_CLASS]; } - unsigned char getDataEncoding() const { return e_ident[ELF::EI_DATA]; } - }; // This flag is used for classof, to distinguish ELFObjectFile from // its subclass. If more subclasses will be created, this flag will // have to become an enum. diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h index c7baaeb9d9..adb1a77ae4 100644 --- a/include/llvm/TableGen/Record.h +++ b/include/llvm/TableGen/Record.h @@ -689,7 +689,7 @@ public: } virtual Init *getBit(unsigned Bit) const { - return BitInit::get((Value & (1 << Bit)) != 0); + return BitInit::get((Value & (1ULL << Bit)) != 0); } }; diff --git a/include/llvm/Target/TargetRegisterInfo.h b/include/llvm/Target/TargetRegisterInfo.h index df4d900e4c..942ee44827 100644 --- a/include/llvm/Target/TargetRegisterInfo.h +++ b/include/llvm/Target/TargetRegisterInfo.h @@ -221,13 +221,17 @@ public: private: const TargetRegisterInfoDesc *InfoDesc; // Extra desc array for codegen const char *const *SubRegIndexNames; // Names of subreg indexes. + // Pointer to array of lane masks, one per sub-reg index. + const unsigned *SubRegIndexLaneMasks; + regclass_iterator RegClassBegin, RegClassEnd; // List of regclasses protected: TargetRegisterInfo(const TargetRegisterInfoDesc *ID, regclass_iterator RegClassBegin, regclass_iterator RegClassEnd, - const char *const *subregindexnames); + const char *const *SRINames, + const unsigned *SRILaneMasks); virtual ~TargetRegisterInfo(); public: @@ -327,10 +331,36 @@ public: /// getSubRegIndexName - Return the human-readable symbolic target-specific /// name for the specified SubRegIndex. const char *getSubRegIndexName(unsigned SubIdx) const { - assert(SubIdx && "This is not a subregister index"); + assert(SubIdx && SubIdx < getNumSubRegIndices() && + "This is not a subregister index"); return SubRegIndexNames[SubIdx-1]; } + /// getSubRegIndexLaneMask - Return a bitmask representing the parts of a + /// register that are covered by SubIdx. + /// + /// Lane masks for sub-register indices are similar to register units for + /// physical registers. The individual bits in a lane mask can't be assigned + /// any specific meaning. They can be used to check if two sub-register + /// indices overlap. + /// + /// If the target has a register such that: + /// + /// getSubReg(Reg, A) overlaps getSubReg(Reg, B) + /// + /// then: + /// + /// getSubRegIndexLaneMask(A) & getSubRegIndexLaneMask(B) != 0 + /// + /// The converse is not necessarily true. If two lane masks have a common + /// bit, the corresponding sub-registers may not overlap, but it can be + /// assumed that they usually will. + unsigned getSubRegIndexLaneMask(unsigned SubIdx) const { + // SubIdx == 0 is allowed, it has the lane mask ~0u. + assert(SubIdx < getNumSubRegIndices() && "This is not a subregister index"); + return SubRegIndexLaneMasks[SubIdx]; + } + /// regsOverlap - Returns true if the two registers are equal or alias each /// other. The registers may be virtual register. bool regsOverlap(unsigned regA, unsigned regB) const { diff --git a/include/llvm/Transforms/IPO.h b/include/llvm/Transforms/IPO.h index 18176e8fdb..962cb63758 100644 --- a/include/llvm/Transforms/IPO.h +++ b/include/llvm/Transforms/IPO.h @@ -192,6 +192,11 @@ ModulePass *createMergeFunctionsPass(); /// createPartialInliningPass - This pass inlines parts of functions. /// ModulePass *createPartialInliningPass(); + +//===----------------------------------------------------------------------===// +// createMetaRenamerPass - Rename everything with metasyntatic names. +// +ModulePass *createMetaRenamerPass(); } // End llvm namespace diff --git a/include/llvm/Transforms/Utils/BypassSlowDivision.h b/include/llvm/Transforms/Utils/BypassSlowDivision.h index 3646d736ab..c262434b66 100644 --- a/include/llvm/Transforms/Utils/BypassSlowDivision.h +++ b/include/llvm/Transforms/Utils/BypassSlowDivision.h @@ -20,10 +20,14 @@ #include "llvm/Function.h" +namespace llvm { + /// This optimization identifies DIV instructions that can be /// profitably bypassed and carried out with a shorter, faster divide. -bool bypassSlowDivision(llvm::Function &F, - llvm::Function::iterator &I, - const llvm::DenseMap<llvm::Type *, llvm::Type *> &BypassTypeMap); +bool bypassSlowDivision(Function &F, + Function::iterator &I, + const DenseMap<Type *, Type *> &BypassTypeMap); + +} // End llvm namespace #endif diff --git a/include/llvm/Transforms/Utils/SimplifyIndVar.h b/include/llvm/Transforms/Utils/SimplifyIndVar.h index 2632d186ff..7e97e218fb 100644 --- a/include/llvm/Transforms/Utils/SimplifyIndVar.h +++ b/include/llvm/Transforms/Utils/SimplifyIndVar.h @@ -21,8 +21,6 @@ namespace llvm { -extern cl::opt<bool> DisableIVRewrite; - class CastInst; class IVUsers; class Loop; |