1311116Sdim//=-- SystemZHazardRecognizer.h - SystemZ Hazard Recognizer -----*- C++ -*-===// 2311116Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6311116Sdim// 7311116Sdim//===----------------------------------------------------------------------===// 8311116Sdim// 9311116Sdim// This file defines a hazard recognizer for the SystemZ scheduler. 10311116Sdim// 11311116Sdim// This class is used by the SystemZ scheduling strategy to maintain 12311116Sdim// the state during scheduling, and provide cost functions for 13311116Sdim// scheduling candidates. This includes: 14311116Sdim// 15311116Sdim// * Decoder grouping. A decoder group can maximally hold 3 uops, and 16311116Sdim// instructions that always begin a new group should be scheduled when 17311116Sdim// the current decoder group is empty. 18311116Sdim// * Processor resources usage. It is beneficial to balance the use of 19311116Sdim// resources. 20311116Sdim// 21327952Sdim// A goal is to consider all instructions, also those outside of any 22327952Sdim// scheduling region. Such instructions are "advanced" past and include 23327952Sdim// single instructions before a scheduling region, branches etc. 24327952Sdim// 25327952Sdim// A block that has only one predecessor continues scheduling with the state 26327952Sdim// of it (which may be updated by emitting branches). 27327952Sdim// 28311116Sdim// ===---------------------------------------------------------------------===// 29311116Sdim 30311116Sdim#include "SystemZHazardRecognizer.h" 31311116Sdim#include "llvm/ADT/Statistic.h" 32311116Sdim 33311116Sdimusing namespace llvm; 34311116Sdim 35321369Sdim#define DEBUG_TYPE "machine-scheduler" 36311116Sdim 37311116Sdim// This is the limit of processor resource usage at which the 38311116Sdim// scheduler should try to look for other instructions (not using the 39311116Sdim// critical resource). 40311116Sdimstatic cl::opt<int> ProcResCostLim("procres-cost-lim", cl::Hidden, 41311116Sdim cl::desc("The OOO window for processor " 42311116Sdim "resources during scheduling."), 43311116Sdim cl::init(8)); 44311116Sdim 45311116Sdimunsigned SystemZHazardRecognizer:: 46311116SdimgetNumDecoderSlots(SUnit *SU) const { 47327952Sdim const MCSchedClassDesc *SC = getSchedClass(SU); 48311116Sdim if (!SC->isValid()) 49311116Sdim return 0; // IMPLICIT_DEF / KILL -- will not make impact in output. 50311116Sdim 51344779Sdim assert((SC->NumMicroOps != 2 || (SC->BeginGroup && !SC->EndGroup)) && 52344779Sdim "Only cracked instruction can have 2 uops."); 53344779Sdim assert((SC->NumMicroOps < 3 || (SC->BeginGroup && SC->EndGroup)) && 54344779Sdim "Expanded instructions always group alone."); 55344779Sdim assert((SC->NumMicroOps < 3 || (SC->NumMicroOps % 3 == 0)) && 56344779Sdim "Expanded instructions fill the group(s)."); 57341825Sdim 58344779Sdim return SC->NumMicroOps; 59311116Sdim} 60311116Sdim 61341825Sdimunsigned SystemZHazardRecognizer::getCurrCycleIdx(SUnit *SU) const { 62311116Sdim unsigned Idx = CurrGroupSize; 63311116Sdim if (GrpCount % 2) 64311116Sdim Idx += 3; 65341825Sdim 66341825Sdim if (SU != nullptr && !fitsIntoCurrentGroup(SU)) { 67341825Sdim if (Idx == 1 || Idx == 2) 68341825Sdim Idx = 3; 69341825Sdim else if (Idx == 4 || Idx == 5) 70341825Sdim Idx = 0; 71341825Sdim } 72341825Sdim 73311116Sdim return Idx; 74311116Sdim} 75311116Sdim 76311116SdimScheduleHazardRecognizer::HazardType SystemZHazardRecognizer:: 77311116SdimgetHazardType(SUnit *m, int Stalls) { 78311116Sdim return (fitsIntoCurrentGroup(m) ? NoHazard : Hazard); 79311116Sdim} 80311116Sdim 81311116Sdimvoid SystemZHazardRecognizer::Reset() { 82311116Sdim CurrGroupSize = 0; 83341825Sdim CurrGroupHas4RegOps = false; 84311116Sdim clearProcResCounters(); 85311116Sdim GrpCount = 0; 86311116Sdim LastFPdOpCycleIdx = UINT_MAX; 87327952Sdim LastEmittedMI = nullptr; 88341825Sdim LLVM_DEBUG(CurGroupDbg = "";); 89311116Sdim} 90311116Sdim 91311116Sdimbool 92311116SdimSystemZHazardRecognizer::fitsIntoCurrentGroup(SUnit *SU) const { 93327952Sdim const MCSchedClassDesc *SC = getSchedClass(SU); 94311116Sdim if (!SC->isValid()) 95311116Sdim return true; 96311116Sdim 97311116Sdim // A cracked instruction only fits into schedule if the current 98311116Sdim // group is empty. 99311116Sdim if (SC->BeginGroup) 100311116Sdim return (CurrGroupSize == 0); 101311116Sdim 102341825Sdim // An instruction with 4 register operands will not fit in last slot. 103341825Sdim assert ((CurrGroupSize < 2 || !CurrGroupHas4RegOps) && 104341825Sdim "Current decoder group is already full!"); 105341825Sdim if (CurrGroupSize == 2 && has4RegOps(SU->getInstr())) 106341825Sdim return false; 107341825Sdim 108311116Sdim // Since a full group is handled immediately in EmitInstruction(), 109311116Sdim // SU should fit into current group. NumSlots should be 1 or 0, 110311116Sdim // since it is not a cracked or expanded instruction. 111311116Sdim assert ((getNumDecoderSlots(SU) <= 1) && (CurrGroupSize < 3) && 112311116Sdim "Expected normal instruction to fit in non-full group!"); 113311116Sdim 114311116Sdim return true; 115311116Sdim} 116311116Sdim 117341825Sdimbool SystemZHazardRecognizer::has4RegOps(const MachineInstr *MI) const { 118341825Sdim const MachineFunction &MF = *MI->getParent()->getParent(); 119341825Sdim const TargetRegisterInfo *TRI = &TII->getRegisterInfo(); 120341825Sdim const MCInstrDesc &MID = MI->getDesc(); 121341825Sdim unsigned Count = 0; 122341825Sdim for (unsigned OpIdx = 0; OpIdx < MID.getNumOperands(); OpIdx++) { 123341825Sdim const TargetRegisterClass *RC = TII->getRegClass(MID, OpIdx, TRI, MF); 124341825Sdim if (RC == nullptr) 125341825Sdim continue; 126341825Sdim if (OpIdx >= MID.getNumDefs() && 127341825Sdim MID.getOperandConstraint(OpIdx, MCOI::TIED_TO) != -1) 128341825Sdim continue; 129341825Sdim Count++; 130341825Sdim } 131341825Sdim return Count >= 4; 132341825Sdim} 133311116Sdim 134341825Sdimvoid SystemZHazardRecognizer::nextGroup() { 135341825Sdim if (CurrGroupSize == 0) 136341825Sdim return; 137311116Sdim 138341825Sdim LLVM_DEBUG(dumpCurrGroup("Completed decode group")); 139341825Sdim LLVM_DEBUG(CurGroupDbg = "";); 140311116Sdim 141344779Sdim int NumGroups = ((CurrGroupSize > 3) ? (CurrGroupSize / 3) : 1); 142344779Sdim assert((CurrGroupSize <= 3 || CurrGroupSize % 3 == 0) && 143344779Sdim "Current decoder group bad."); 144311116Sdim 145341825Sdim // Reset counter for next group. 146341825Sdim CurrGroupSize = 0; 147341825Sdim CurrGroupHas4RegOps = false; 148311116Sdim 149344779Sdim GrpCount += ((unsigned) NumGroups); 150344779Sdim 151344779Sdim // Decrease counters for execution units. 152341825Sdim for (unsigned i = 0; i < SchedModel->getNumProcResourceKinds(); ++i) 153344779Sdim ProcResourceCounters[i] = ((ProcResourceCounters[i] > NumGroups) 154344779Sdim ? (ProcResourceCounters[i] - NumGroups) 155344779Sdim : 0); 156341825Sdim 157341825Sdim // Clear CriticalResourceIdx if it is now below the threshold. 158341825Sdim if (CriticalResourceIdx != UINT_MAX && 159341825Sdim (ProcResourceCounters[CriticalResourceIdx] <= 160341825Sdim ProcResCostLim)) 161341825Sdim CriticalResourceIdx = UINT_MAX; 162341825Sdim 163341825Sdim LLVM_DEBUG(dumpState();); 164311116Sdim} 165311116Sdim 166311116Sdim#ifndef NDEBUG // Debug output 167311116Sdimvoid SystemZHazardRecognizer::dumpSU(SUnit *SU, raw_ostream &OS) const { 168311116Sdim OS << "SU(" << SU->NodeNum << "):"; 169327952Sdim OS << TII->getName(SU->getInstr()->getOpcode()); 170311116Sdim 171327952Sdim const MCSchedClassDesc *SC = getSchedClass(SU); 172311116Sdim if (!SC->isValid()) 173311116Sdim return; 174341825Sdim 175311116Sdim for (TargetSchedModel::ProcResIter 176311116Sdim PI = SchedModel->getWriteProcResBegin(SC), 177311116Sdim PE = SchedModel->getWriteProcResEnd(SC); PI != PE; ++PI) { 178311116Sdim const MCProcResourceDesc &PRD = 179311116Sdim *SchedModel->getProcResource(PI->ProcResourceIdx); 180311116Sdim std::string FU(PRD.Name); 181311116Sdim // trim e.g. Z13_FXaUnit -> FXa 182311116Sdim FU = FU.substr(FU.find("_") + 1); 183341825Sdim size_t Pos = FU.find("Unit"); 184341825Sdim if (Pos != std::string::npos) 185341825Sdim FU.resize(Pos); 186341825Sdim if (FU == "LS") // LSUnit -> LSU 187341825Sdim FU = "LSU"; 188311116Sdim OS << "/" << FU; 189311116Sdim 190311116Sdim if (PI->Cycles > 1) 191311116Sdim OS << "(" << PI->Cycles << "cyc)"; 192311116Sdim } 193311116Sdim 194311116Sdim if (SC->NumMicroOps > 1) 195311116Sdim OS << "/" << SC->NumMicroOps << "uops"; 196311116Sdim if (SC->BeginGroup && SC->EndGroup) 197311116Sdim OS << "/GroupsAlone"; 198311116Sdim else if (SC->BeginGroup) 199311116Sdim OS << "/BeginsGroup"; 200311116Sdim else if (SC->EndGroup) 201311116Sdim OS << "/EndsGroup"; 202311116Sdim if (SU->isUnbuffered) 203311116Sdim OS << "/Unbuffered"; 204341825Sdim if (has4RegOps(SU->getInstr())) 205341825Sdim OS << "/4RegOps"; 206311116Sdim} 207311116Sdim 208311116Sdimvoid SystemZHazardRecognizer::dumpCurrGroup(std::string Msg) const { 209341825Sdim dbgs() << "++ " << Msg; 210311116Sdim dbgs() << ": "; 211311116Sdim 212311116Sdim if (CurGroupDbg.empty()) 213311116Sdim dbgs() << " <empty>\n"; 214311116Sdim else { 215311116Sdim dbgs() << "{ " << CurGroupDbg << " }"; 216311116Sdim dbgs() << " (" << CurrGroupSize << " decoder slot" 217311116Sdim << (CurrGroupSize > 1 ? "s":"") 218341825Sdim << (CurrGroupHas4RegOps ? ", 4RegOps" : "") 219311116Sdim << ")\n"; 220311116Sdim } 221311116Sdim} 222311116Sdim 223311116Sdimvoid SystemZHazardRecognizer::dumpProcResourceCounters() const { 224311116Sdim bool any = false; 225311116Sdim 226311116Sdim for (unsigned i = 0; i < SchedModel->getNumProcResourceKinds(); ++i) 227311116Sdim if (ProcResourceCounters[i] > 0) { 228311116Sdim any = true; 229311116Sdim break; 230311116Sdim } 231311116Sdim 232311116Sdim if (!any) 233311116Sdim return; 234311116Sdim 235341825Sdim dbgs() << "++ | Resource counters: "; 236311116Sdim for (unsigned i = 0; i < SchedModel->getNumProcResourceKinds(); ++i) 237341825Sdim if (ProcResourceCounters[i] > 0) 238341825Sdim dbgs() << SchedModel->getProcResource(i)->Name 239341825Sdim << ":" << ProcResourceCounters[i] << " "; 240341825Sdim dbgs() << "\n"; 241341825Sdim 242341825Sdim if (CriticalResourceIdx != UINT_MAX) 243341825Sdim dbgs() << "++ | Critical resource: " 244341825Sdim << SchedModel->getProcResource(CriticalResourceIdx)->Name 245341825Sdim << "\n"; 246311116Sdim} 247341825Sdim 248341825Sdimvoid SystemZHazardRecognizer::dumpState() const { 249341825Sdim dumpCurrGroup("| Current decoder group"); 250341825Sdim dbgs() << "++ | Current cycle index: " 251341825Sdim << getCurrCycleIdx() << "\n"; 252341825Sdim dumpProcResourceCounters(); 253341825Sdim if (LastFPdOpCycleIdx != UINT_MAX) 254341825Sdim dbgs() << "++ | Last FPd cycle index: " << LastFPdOpCycleIdx << "\n"; 255341825Sdim} 256341825Sdim 257311116Sdim#endif //NDEBUG 258311116Sdim 259311116Sdimvoid SystemZHazardRecognizer::clearProcResCounters() { 260311116Sdim ProcResourceCounters.assign(SchedModel->getNumProcResourceKinds(), 0); 261311116Sdim CriticalResourceIdx = UINT_MAX; 262311116Sdim} 263311116Sdim 264327952Sdimstatic inline bool isBranchRetTrap(MachineInstr *MI) { 265327952Sdim return (MI->isBranch() || MI->isReturn() || 266327952Sdim MI->getOpcode() == SystemZ::CondTrap); 267327952Sdim} 268327952Sdim 269311116Sdim// Update state with SU as the next scheduled unit. 270311116Sdimvoid SystemZHazardRecognizer:: 271311116SdimEmitInstruction(SUnit *SU) { 272327952Sdim const MCSchedClassDesc *SC = getSchedClass(SU); 273341825Sdim LLVM_DEBUG(dbgs() << "++ HazardRecognizer emitting "; dumpSU(SU, dbgs()); 274341825Sdim dbgs() << "\n";); 275341825Sdim LLVM_DEBUG(dumpCurrGroup("Decode group before emission");); 276311116Sdim 277311116Sdim // If scheduling an SU that must begin a new decoder group, move on 278311116Sdim // to next group. 279311116Sdim if (!fitsIntoCurrentGroup(SU)) 280311116Sdim nextGroup(); 281311116Sdim 282341825Sdim LLVM_DEBUG(raw_string_ostream cgd(CurGroupDbg); 283341825Sdim if (CurGroupDbg.length()) cgd << ", "; dumpSU(SU, cgd);); 284311116Sdim 285327952Sdim LastEmittedMI = SU->getInstr(); 286327952Sdim 287311116Sdim // After returning from a call, we don't know much about the state. 288327952Sdim if (SU->isCall) { 289341825Sdim LLVM_DEBUG(dbgs() << "++ Clearing state after call.\n";); 290341825Sdim Reset(); 291341825Sdim LastEmittedMI = SU->getInstr(); 292311116Sdim return; 293311116Sdim } 294311116Sdim 295311116Sdim // Increase counter for execution unit(s). 296311116Sdim for (TargetSchedModel::ProcResIter 297311116Sdim PI = SchedModel->getWriteProcResBegin(SC), 298311116Sdim PE = SchedModel->getWriteProcResEnd(SC); PI != PE; ++PI) { 299311116Sdim // Don't handle FPd together with the other resources. 300311116Sdim if (SchedModel->getProcResource(PI->ProcResourceIdx)->BufferSize == 1) 301311116Sdim continue; 302311116Sdim int &CurrCounter = 303311116Sdim ProcResourceCounters[PI->ProcResourceIdx]; 304311116Sdim CurrCounter += PI->Cycles; 305311116Sdim // Check if this is now the new critical resource. 306311116Sdim if ((CurrCounter > ProcResCostLim) && 307311116Sdim (CriticalResourceIdx == UINT_MAX || 308311116Sdim (PI->ProcResourceIdx != CriticalResourceIdx && 309311116Sdim CurrCounter > 310311116Sdim ProcResourceCounters[CriticalResourceIdx]))) { 311341825Sdim LLVM_DEBUG( 312341825Sdim dbgs() << "++ New critical resource: " 313341825Sdim << SchedModel->getProcResource(PI->ProcResourceIdx)->Name 314341825Sdim << "\n";); 315311116Sdim CriticalResourceIdx = PI->ProcResourceIdx; 316311116Sdim } 317311116Sdim } 318311116Sdim 319311116Sdim // Make note of an instruction that uses a blocking resource (FPd). 320311116Sdim if (SU->isUnbuffered) { 321341825Sdim LastFPdOpCycleIdx = getCurrCycleIdx(SU); 322341825Sdim LLVM_DEBUG(dbgs() << "++ Last FPd cycle index: " << LastFPdOpCycleIdx 323341825Sdim << "\n";); 324311116Sdim } 325311116Sdim 326311116Sdim // Insert SU into current group by increasing number of slots used 327311116Sdim // in current group. 328311116Sdim CurrGroupSize += getNumDecoderSlots(SU); 329341825Sdim CurrGroupHas4RegOps |= has4RegOps(SU->getInstr()); 330344779Sdim unsigned GroupLim = (CurrGroupHas4RegOps ? 2 : 3); 331344779Sdim assert((CurrGroupSize <= GroupLim || CurrGroupSize == getNumDecoderSlots(SU)) 332344779Sdim && "SU does not fit into decoder group!"); 333311116Sdim 334311116Sdim // Check if current group is now full/ended. If so, move on to next 335311116Sdim // group to be ready to evaluate more candidates. 336344779Sdim if (CurrGroupSize >= GroupLim || SC->EndGroup) 337311116Sdim nextGroup(); 338311116Sdim} 339311116Sdim 340311116Sdimint SystemZHazardRecognizer::groupingCost(SUnit *SU) const { 341327952Sdim const MCSchedClassDesc *SC = getSchedClass(SU); 342311116Sdim if (!SC->isValid()) 343311116Sdim return 0; 344341825Sdim 345311116Sdim // If SU begins new group, it can either break a current group early 346311116Sdim // or fit naturally if current group is empty (negative cost). 347311116Sdim if (SC->BeginGroup) { 348311116Sdim if (CurrGroupSize) 349311116Sdim return 3 - CurrGroupSize; 350311116Sdim return -1; 351311116Sdim } 352311116Sdim 353311116Sdim // Similarly, a group-ending SU may either fit well (last in group), or 354311116Sdim // end the group prematurely. 355311116Sdim if (SC->EndGroup) { 356311116Sdim unsigned resultingGroupSize = 357311116Sdim (CurrGroupSize + getNumDecoderSlots(SU)); 358311116Sdim if (resultingGroupSize < 3) 359311116Sdim return (3 - resultingGroupSize); 360311116Sdim return -1; 361311116Sdim } 362311116Sdim 363341825Sdim // An instruction with 4 register operands will not fit in last slot. 364341825Sdim if (CurrGroupSize == 2 && has4RegOps(SU->getInstr())) 365341825Sdim return 1; 366341825Sdim 367311116Sdim // Most instructions can be placed in any decoder slot. 368311116Sdim return 0; 369311116Sdim} 370311116Sdim 371341825Sdimbool SystemZHazardRecognizer::isFPdOpPreferred_distance(SUnit *SU) const { 372311116Sdim assert (SU->isUnbuffered); 373311116Sdim // If this is the first FPd op, it should be scheduled high. 374311116Sdim if (LastFPdOpCycleIdx == UINT_MAX) 375311116Sdim return true; 376311116Sdim // If this is not the first PFd op, it should go into the other side 377311116Sdim // of the processor to use the other FPd unit there. This should 378311116Sdim // generally happen if two FPd ops are placed with 2 other 379311116Sdim // instructions between them (modulo 6). 380341825Sdim unsigned SUCycleIdx = getCurrCycleIdx(SU); 381341825Sdim if (LastFPdOpCycleIdx > SUCycleIdx) 382341825Sdim return ((LastFPdOpCycleIdx - SUCycleIdx) == 3); 383341825Sdim return ((SUCycleIdx - LastFPdOpCycleIdx) == 3); 384311116Sdim} 385311116Sdim 386311116Sdimint SystemZHazardRecognizer:: 387311116SdimresourcesCost(SUnit *SU) { 388311116Sdim int Cost = 0; 389311116Sdim 390327952Sdim const MCSchedClassDesc *SC = getSchedClass(SU); 391311116Sdim if (!SC->isValid()) 392311116Sdim return 0; 393311116Sdim 394311116Sdim // For a FPd op, either return min or max value as indicated by the 395311116Sdim // distance to any prior FPd op. 396311116Sdim if (SU->isUnbuffered) 397311116Sdim Cost = (isFPdOpPreferred_distance(SU) ? INT_MIN : INT_MAX); 398311116Sdim // For other instructions, give a cost to the use of the critical resource. 399311116Sdim else if (CriticalResourceIdx != UINT_MAX) { 400311116Sdim for (TargetSchedModel::ProcResIter 401311116Sdim PI = SchedModel->getWriteProcResBegin(SC), 402311116Sdim PE = SchedModel->getWriteProcResEnd(SC); PI != PE; ++PI) 403311116Sdim if (PI->ProcResourceIdx == CriticalResourceIdx) 404311116Sdim Cost = PI->Cycles; 405311116Sdim } 406311116Sdim 407311116Sdim return Cost; 408311116Sdim} 409311116Sdim 410327952Sdimvoid SystemZHazardRecognizer::emitInstruction(MachineInstr *MI, 411327952Sdim bool TakenBranch) { 412327952Sdim // Make a temporary SUnit. 413327952Sdim SUnit SU(MI, 0); 414327952Sdim 415327952Sdim // Set interesting flags. 416327952Sdim SU.isCall = MI->isCall(); 417327952Sdim 418327952Sdim const MCSchedClassDesc *SC = SchedModel->resolveSchedClass(MI); 419327952Sdim for (const MCWriteProcResEntry &PRE : 420327952Sdim make_range(SchedModel->getWriteProcResBegin(SC), 421327952Sdim SchedModel->getWriteProcResEnd(SC))) { 422327952Sdim switch (SchedModel->getProcResource(PRE.ProcResourceIdx)->BufferSize) { 423327952Sdim case 0: 424327952Sdim SU.hasReservedResource = true; 425327952Sdim break; 426327952Sdim case 1: 427327952Sdim SU.isUnbuffered = true; 428327952Sdim break; 429327952Sdim default: 430327952Sdim break; 431327952Sdim } 432327952Sdim } 433327952Sdim 434341825Sdim unsigned GroupSizeBeforeEmit = CurrGroupSize; 435327952Sdim EmitInstruction(&SU); 436327952Sdim 437341825Sdim if (!TakenBranch && isBranchRetTrap(MI)) { 438341825Sdim // NT Branch on second slot ends group. 439341825Sdim if (GroupSizeBeforeEmit == 1) 440341825Sdim nextGroup(); 441341825Sdim } 442341825Sdim 443327952Sdim if (TakenBranch && CurrGroupSize > 0) 444341825Sdim nextGroup(); 445327952Sdim 446327952Sdim assert ((!MI->isTerminator() || isBranchRetTrap(MI)) && 447327952Sdim "Scheduler: unhandled terminator!"); 448327952Sdim} 449327952Sdim 450327952Sdimvoid SystemZHazardRecognizer:: 451327952SdimcopyState(SystemZHazardRecognizer *Incoming) { 452327952Sdim // Current decoder group 453327952Sdim CurrGroupSize = Incoming->CurrGroupSize; 454341825Sdim LLVM_DEBUG(CurGroupDbg = Incoming->CurGroupDbg;); 455327952Sdim 456327952Sdim // Processor resources 457327952Sdim ProcResourceCounters = Incoming->ProcResourceCounters; 458327952Sdim CriticalResourceIdx = Incoming->CriticalResourceIdx; 459327952Sdim 460327952Sdim // FPd 461327952Sdim LastFPdOpCycleIdx = Incoming->LastFPdOpCycleIdx; 462327952Sdim GrpCount = Incoming->GrpCount; 463327952Sdim} 464