3.5.1.3. 初步构建调度类型
每条指令定义中的成员Itinerary是InstrItinClass类型,用以描述该指令的执行步骤。前面我们已经看过一些X86指令定义的例子,其中成员Itinerary被设置为InstrItinClass的派生定义,比如用在LOCK_ArithBinOp8mr的IIC_ALU_NONMEM。
另外,指令定义里的成员SchedRW(类型list<SchedReadWrite>)则是描述该指令对资源的占用。它也可以用作指令调度依据。
对指令来说,成员Itinerary以及SchedRW将唯一地确定指令的调度类型,这些成员相同的指令应该具有相同的调度类型。
496 void CodeGenSchedModels::collectSchedClasses(){
497
498 // NoItinerary isalways the first class at Idx=0
499 SchedClasses.resize(1);
500 SchedClasses.back().Index = 0;
501 SchedClasses.back().Name ="NoInstrModel";
502 SchedClasses.back().ItinClassDef =Records.getDef("NoItinerary");
503 SchedClasses.back().ProcIndices.push_back(0);
504
505 // Create aSchedClass for each unique combination of itinerary class and
506 // SchedRW list.
507 for (const CodeGenInstruction *Inst :Target.instructions()) {
508 Record *ItinDef =Inst->TheDef->getValueAsDef("Itinerary");
509 IdxVec Writes, Reads;
510 if(!Inst->TheDef->isValueUnset("SchedRW"))
511 findRWs(Inst->TheDef->getValueAsListOfDefs("SchedRW"),Writes, Reads);
512
513 // ProcIdx == 0indicates the class applies to all processors.
514 IdxVec ProcIndices(1, 0);
515
516 unsigned SCIdx = addSchedClass(ItinDef, Writes,Reads, ProcIndices);
517 InstrClassMap[Inst->TheDef] = SCIdx;
518 }
519 // Create classesfor InstRW defs.
520 RecVec InstRWDefs =Records.getAllDerivedDefinitions("InstRW");
521 std::sort(InstRWDefs.begin(),InstRWDefs.end(), LessRecord());
522 for (RecIterOI = InstRWDefs.begin(), OE = InstRWDefs.end(); OI != OE; ++OI)
523 createInstRWClass(*OI);
524
525 NumInstrSchedClasses = SchedClasses.size();
526
527 bool EnableDump = false;
528 DEBUG(EnableDump = true);
529 if (!EnableDump)
530 return;
531
532 for (const CodeGenInstruction *Inst :Target.instructions()) {
533 std::string InstName =Inst->TheDef->getName();
534 unsigned SCIdx =InstrClassMap.lookup(Inst->TheDef);
535 if (!SCIdx) {
536 dbgs() << "No machine modelfor " << Inst->TheDef->getName() << '\n';
537 continue;
538 }
539 CodeGenSchedClass &SC =getSchedClass(SCIdx);
540 if (SC.ProcIndices[0] != 0)
541 PrintFatalError(Inst->TheDef->getLoc(), "Instruction's schedclass "
542 "must not besubtarget specific.");
543
544 IdxVec ProcIndices;
545 if (SC.ItinClassDef->getName() !="NoItinerary") {
546 ProcIndices.push_back(0);
547 dbgs() << "Itinerary for" << InstName << ": "
548 <<SC.ItinClassDef->getName() << '\n';
549 }
550 if (!SC.Writes.empty()) {
551 ProcIndices.push_back(0);
552 dbgs() << "SchedRW machinemodel for " << InstName;
553 for(IdxIter WI = SC.Writes.begin(), WE = SC.Writes.end(); WI != WE; ++WI)
554 dbgs() << " " <<SchedWrites[*WI].Name;
555 for(IdxIter RI = SC.Reads.begin(), RE = SC.Reads.end(); RI != RE; ++RI)
556 dbgs() << " " <<SchedReads[*RI].Name;
557 dbgs() << '\n';
558 }
559 constRecVec &RWDefs = SchedClasses[SCIdx].InstRWs;
560 for(RecIter RWI = RWDefs.begin(), RWE = RWDefs.end();
561 RWI != RWE; ++RWI) {
562 constCodeGenProcModel &ProcModel =
563 getProcModel((*RWI)->getValueAsDef("SchedModel"));
564 ProcIndices.push_back(ProcModel.Index);
565 dbgs() << "InstRW on "<< ProcModel.ModelName << " for " << InstName;
566 IdxVec Writes;
567 IdxVec Reads;
568 findRWs((*RWI)->getValueAsListOfDefs("OperandReadWrites"),
569 Writes, Reads);
570 for(IdxIter WI = Writes.begin(), WE = Writes.end(); WI != WE; ++WI)
571 dbgs() << " " <<SchedWrites[*WI].Name;
572 for(IdxIter RI = Reads.begin(), RE = Reads.end(); RI != RE; ++RI)
573 dbgs() << " " <<SchedReads[*RI].Name;
574 dbgs() << '\n';
575 }
576 for(std::vector<CodeGenProcModel>::iterator PI = ProcModels.begin(),
577 PE = ProcModels.end(); PI != PE;++PI) {
578 if (!std::count(ProcIndices.begin(),ProcIndices.end(), PI->Index))
579 dbgs() << "No machine model for" << Inst->TheDef->getName()
580 << " on processor" << PI->ModelName << '\n';
581 }
582 }
583 }
CodeGenSchedModels的容器SchedClasses的类型是std::vector<CodeGenSchedClass>,其中类型CodeGenSchedClass用于保存描述一个调度类型所需的信息。它的定义如下:
127 structCodeGenSchedClass {
128 unsigned Index;
129 std::string Name;
130 Record *ItinClassDef;
131
132 IdxVec Writes;
133 IdxVec Reads;
134 // Sorted list ofProcIdx, where ProcIdx==0 implies any processor.
135 IdxVec ProcIndices;
136
137 std::vector<CodeGenSchedTransition>Transitions;
138
139 // InstRW recordsassociated with this class. These records may refer to an
140 // Instruction nolonger mapped to this class by InstrClassMap. These
141 // Instructionsshould be ignored by this class because they have been split
142 // off to joinanother inferred class.
143 RecVec InstRWs;
144
145 CodeGenSchedClass(): Index(0),ItinClassDef(nullptr) {}
146
147 bool isKeyEqual(Record *IC, const IdxVec &W, const IdxVec&R) {
148 returnItinClassDef == IC && Writes == W && Reads == R;
149 }
150
151 // Is this classgenerated from a variants if existing classes? Instructions
152 // are nevermapped directly to inferred scheduling classes.
153 bool isInferred() const{ return !ItinClassDef; }
154
155 #ifndef NDEBUG
156 void dump(constCodeGenSchedModels *SchedModels) const;
157 #endif
158 };
每条指令都将映射到一个调度类型。一共有四种调度类型:
1)一个显式定义的行程,会设置ItinClassDef(来自指令定义)。并且ProcIndices都是0(表示适用于对于所有的处理器)。
2)由一个指令定义中一组SchedWrites与SchedRead所描述的隐含类型,它被所有的次级目标机器共享。
3)对指定的处理器,以将指令映射到一组SchedWrite与SchedRead的InstRW定义描述的隐含类型。容器InstrClassMap应该将这些指令映射到这个类型。ProcIndices包含所有提供该类型相关InstrRW的处理器。没有InstRW定义的处理器仍然可以定义ItinClassDef,Writes或Reads。
4)代表可能在运行时解析的另一个类别变种的推导类型。ProcIndices包含可能要求该类别的处理器。在展开这个变种时在SchedClass间传播这个ProcIndices。可以从一个itinerary类别推导出多个SchedClass,每一个实例都从将该itinerary类别映射到Writes或Reads变种的ItinRW记录中继承处理器索引。
首先构建的是对应NoItinerary定义的调度类,在503行CodeGenSchedClass的ProcIndices容器记录了对应CodeGenProcModel对象的索引。
接着,在507行循环处理Instruction->Itinerary->SchedRW域,下面的findRWs方法获取该SchedRW域内援引的SchedReadWrite定义(如果它们有设置的话):
382 void CodeGenSchedModels::findRWs(const RecVec &RWDefs,
383 IdxVec&Writes, IdxVec &Reads) const {
384 RecVec WriteDefs;
385 RecVec ReadDefs;
386 splitSchedReadWrites(RWDefs,WriteDefs, ReadDefs);
387 findRWs(WriteDefs,Writes, false);
388 findRWs(ReadDefs, Reads, true);
389 }
首先由辅助函数splitSchedReadWrites区分SchedWrite及SchedRead定义。
367 namespace llvm {
368 void splitSchedReadWrites(constRecVec &RWDefs,
369 RecVec&WriteDefs, RecVec &ReadDefs) {
370 for (RecIterRWI = RWDefs.begin(), RWE = RWDefs.end(); RWI != RWE; ++RWI) {
371 if((*RWI)->isSubClassOf("SchedWrite"))
372 WriteDefs.push_back(*RWI);
373 else {
374 assert((*RWI)->isSubClassOf("SchedRead")&& "unknown SchedReadWrite");
375 ReadDefs.push_back(*RWI);
376 }
377 }
378 }
379 } //namespace llvm
在上面387与388行,调用findRWs将这些被援引SchedWrite及SchedRead定义在SchedReads或SchedWrites容器里的索引记录到临时容器Writes与Reads。接着,Writes与Reads作为参数传递给下面的方法。
638 unsigned CodeGenSchedModels::addSchedClass(Record*ItinClassDef,
639 const IdxVec &OperWrites,
640 const IdxVec &OperReads,
641 const IdxVec &ProcIndices)
642 {
643 assert(!ProcIndices.empty()&& "expect at least one ProcIdx");
644
645 unsigned Idx =findSchedClassIdx(ItinClassDef, OperWrites, OperReads);
646 if (Idx || SchedClasses[0].isKeyEqual(ItinClassDef, OperWrites,OperReads)) {
647 IdxVec PI;
648 std::set_union(SchedClasses[Idx].ProcIndices.begin(),
649 SchedClasses[Idx].ProcIndices.end(),
650 ProcIndices.begin(),ProcIndices.end(),
651 std::back_inserter(PI));
652 SchedClasses[Idx].ProcIndices.swap(PI);
653 return Idx;
654 }
655 Idx = SchedClasses.size();
656 SchedClasses.resize(Idx+1);
657 CodeGenSchedClass &SC =SchedClasses.back();
658 SC.Index = Idx;
659 SC.Name = createSchedClassName(ItinClassDef,OperWrites, OperReads);
660 SC.ItinClassDef = ItinClassDef;
661 SC.Writes = OperWrites;
662 SC.Reads = OperReads;
663 SC.ProcIndices = ProcIndices;
664
665 return Idx;
666 }
另外,参数ProcIndices保存了与这个调度类型相关的CodeGenProcModel对象的索引,索引0处的对象代表NoSchedModel与NoItineraries。
CodeGenSchedClass对象是否相同取决于成员ItinClassDef,Writes及Reads的内容决定。645行的findSchedClassIdx方法遍历SchedClasses容器,查找包含ItinClassDef,OperWrites,OperReads的CodeGenSchedClass对象索引,若不存在这样的对象,则返回0。因为SchedClasses[0]是有效的,代表NoItinerary。因此在findSchedClassIdx返回0时,还需检查这个调度类是否就是NoItinerary。
另外,由于CodeGenSchedClass对象唯一取决于成员ItinClassDef,Writes及Reads的内容,因此它的命字也是由这些成员包含的对象名合成的。
如果这样的CodeGenSchedClass对象尚未生成,addSchedClass在SchedClasses容器里构建一个。addSchedClass返回这个调度类的索引,容器InstrClassMap(DenseMap<Record*,unsigned>)将这个索引与指令的Record对象关联起来。
CodeGenSchedModels::collectSchedClasses接下来处理InstRW定义。InstRW可以改变一组指令的SchedRead与SchedWrite的设定。
在522行循环,在对所有InstRW定义的Record对象按名字排序后,依次对它们调用下面的方法,根据InstRW定义构建可能需要的新调度类型。CodeGenSchedClass的容器InstRWs如果不为空,表示这个CodeGenSchedClass对象是因为这个InstRW定义而产生的。
670 void CodeGenSchedModels::createInstRWClass(Record*InstRWDef) {
671 // ClassInstrswill hold an entry for each subset of Instrs in InstRWDef that
672 // intersectswith an existing class via a previous InstRWDef. Instrs that do
673 // not intersectwith an existing class refer back to their former class as
674 // determinedfrom ItinDef or SchedRW.
675 SmallVector<std::pair<unsigned,SmallVector<Record *, 8> >, 4> ClassInstrs;
676 // Sort Instrsinto sets.
677 const RecVec*InstDefs = Sets.expand(InstRWDef);
678 if (InstDefs->empty())
679 PrintFatalError(InstRWDef->getLoc(),"No matching instruction opcodes");
680
681 for (RecIterI = InstDefs->begin(), E = InstDefs->end(); I != E; ++I) {
682 InstClassMapTy::const_iterator Pos =InstrClassMap.find(*I);
683 if (Pos == InstrClassMap.end())
684 PrintFatalError((*I)->getLoc(),"No sched class for instruction.");
685 unsigned SCIdx = Pos->second;
686 unsigned CIdx = 0, CEnd = ClassInstrs.size();
687 for (; CIdx!= CEnd; ++CIdx) {
688 if (ClassInstrs[CIdx].first == SCIdx)
689 break;
690 }
691 if (CIdx == CEnd) {
692 ClassInstrs.resize(CEnd + 1);
693 ClassInstrs[CIdx].first = SCIdx;
694 }
695 ClassInstrs[CIdx].second.push_back(*I);
696 }
697 // For each setof Instrs, create a new class if necessary, and map or remap
698 // the Instrs toit.
699 unsigned CIdx = 0, CEnd = ClassInstrs.size();
700 for (; CIdx!= CEnd; ++CIdx) {
701 unsigned OldSCIdx =ClassInstrs[CIdx].first;
702 ArrayRef<Record*> InstDefs =ClassInstrs[CIdx].second;
703 // If the allinstrs in the current class are accounted for, then leave
704 // them mappedto their old class.
705 if (OldSCIdx) {
706 constRecVec &RWDefs = SchedClasses[OldSCIdx].InstRWs;
707 if (!RWDefs.empty()) {
708 constRecVec *OrigInstDefs = Sets.expand(RWDefs[0]);
709 unsigned OrigNumInstrs = 0;
710 for(RecIter I = OrigInstDefs->begin(), E = OrigInstDefs->end();
711 I != E; ++I) {
712 if (InstrClassMap[*I] == OldSCIdx)
713 ++OrigNumInstrs;
714 }
715 if (OrigNumInstrs == InstDefs.size()) {
716 assert(SchedClasses[OldSCIdx].ProcIndices[0]== 0 &&
717 "expected a genericSchedClass");
718 DEBUG(dbgs() << "InstRW:Reuse SC " << OldSCIdx << ":"
719 <<SchedClasses[OldSCIdx].Name << " on "
720 <<InstRWDef->getValueAsDef("SchedModel")->getName() <<"\n");
721 SchedClasses[OldSCIdx].InstRWs.push_back(InstRWDef);
722 continue;
723 }
724 }
725 }
726 unsigned SCIdx = SchedClasses.size();
727 SchedClasses.resize(SCIdx+1);
728 CodeGenSchedClass &SC =SchedClasses.back();
729 SC.Index = SCIdx;
730 SC.Name = createSchedClassName(InstDefs);
731 DEBUG(dbgs() << "InstRW: New SC" << SCIdx << ":" << SC.Name << "on "
732 <<InstRWDef->getValueAsDef("SchedModel")->getName() <<"\n");
733
734 // PreserveItinDef and Writes/Reads for processors without an InstRW entry.
735 SC.ItinClassDef =SchedClasses[OldSCIdx].ItinClassDef;
736 SC.Writes = SchedClasses[OldSCIdx].Writes;
737 SC.Reads = SchedClasses[OldSCIdx].Reads;
738 SC.ProcIndices.push_back(0);
739 // Map eachInstr to this new class.
740 // Note thatInstDefs may be a smaller list than InstRWDef's "Instrs".
741 Record *RWModelDef =InstRWDef->getValueAsDef("SchedModel");
742 SmallSet<unsigned, 4>RemappedClassIDs;
743 for(ArrayRef<Record*>::const_iterator
744 II = InstDefs.begin(), IE =InstDefs.end(); II != IE; ++II) {
745 unsigned OldSCIdx = InstrClassMap[*II];
746 if (OldSCIdx &&RemappedClassIDs.insert(OldSCIdx).second) {
747 for(RecIter RI = SchedClasses[OldSCIdx].InstRWs.begin(),
748 RE =SchedClasses[OldSCIdx].InstRWs.end(); RI != RE; ++RI) {
749 if((*RI)->getValueAsDef("SchedModel") == RWModelDef) {
750 PrintFatalError(InstRWDef->getLoc(), "Overlapping InstRW def" +
751 (*II)->getName() +" also matches " +
752 (*RI)->getValue("Instrs")->getValue()->getAsString());
753 }
754 assert(*RI!= InstRWDef && "SchedClass has duplicate InstRW def");
755 SC.InstRWs.push_back(*RI);
756 }
757 }
758 InstrClassMap[*II] = SCIdx;
759 }
760 SC.InstRWs.push_back(InstRWDef);
761 }
762 }
InstRW定义给出了一组指令,将它们的调度类重载为指定的一组SchedReadWrite定义。临时容器ClassInstrs用于将这些指令与它们原有的调度类关联起来。
InstRW定义通过dag来指定这组指令,这个dag值是一个匹配指令操作码的正则表达式(dag的操作符必须是instregex),因此首先在677行展开这个dag值。这个表达式由InstRegexOp处理,得到对应的Record对象。
接着,681行循环找出这些指令原来指派的调度类型。collectSchedClasses在前面已经将指令的Record实例与CodeGenSchedClass实例的序号通过InstrClassMap关联起来。因此,682行的查找一定成功。使用临时容器ClassInstrs,是因为可能有多条指令属于同一个调度类型。
接着,在700行遍历原来指派给InstRW指定指令的调度类型。调度类型CodeGenSchedClass对象的容器InstRWs,如果不为空,记录了生成该调度类型的InstRW定义(最后一项)以及它所覆盖的InstRW定义。容器InstrClassMap则关联了现有的指令与调度类型。那么,如果原来调度类型由一个指令为当前InstRW定义所涵盖的InstRW定义生成,且这个调度类型是一个通用类型,可以重用这个调度类型的CodeGenSchedClass对象(715~721行,即原来调度类型被覆盖)。否则,在726以下根据原来的调度类型,生成一个新的CodeGenSchedClass对象来表述这个新的调度类型。注意,这个新的CodeGenSchedClass对象的ProcIndices当前只包含0(738行)。
注意在730行,因为这些调度类型由一组InstRW定义唯一确定,因此它们的名字就是这些InstRW的名字组合。
回到collectSchedClasses,从527行开始的代码大部分是用于调试的目的。
3.5.1.4. 执行步骤的关联
某些处理器,像Atom,通过执行步骤来描述指令调度,这时,在其Processor定义的ProcItin(类型ProcessorItineraries)的IID域(类型list<InstrItinData>)给出了一组执行步骤的描述(使用哪些资源,时延等。参考ATOM的描述一节)。在指令的定义中通过成员Itinerary(类型InstrItinClass)指定所适用的执行步骤。前面,看到指令的执行步骤是确定指令所属调度类型的一个重要组成,对处理器的CodeGenProcModel对象而言,其容器ItinDefList关联起了执行步骤与相关的调度类型。现在,通过下面的CodeGenSchedModels::collectProcItins填充这个容器。
775 void CodeGenSchedModels::collectProcItins(){
776 for(CodeGenProcModel &ProcModel : ProcModels) {
777 if (!ProcModel.hasItineraries())
778 continue;
779
780 RecVec ItinRecords =ProcModel.ItinsDef->getValueAsListOfDefs("IID");
781 assert(!ItinRecords.empty()&& "ProcModel.hasItineraries is incorrect");
782
783 // PopulateItinDefList with Itinerary records.
784 ProcModel.ItinDefList.resize(NumInstrSchedClasses);
785
786 // Insert eachitinerary data record in the correct position within
787 // theprocessor model's ItinDefList.
788 for(unsigned i = 0, N = ItinRecords.size(); i < N; i++) {
789 Record *ItinData = ItinRecords[i];
790 Record *ItinDef =ItinData->getValueAsDef("TheClass");
791 bool FoundClass = false;
792 for(SchedClassIter SCI = schedClassBegin(), SCE = schedClassEnd();
793 SCI != SCE; ++SCI) {
794 // MultipleSchedClasses may share an itinerary. Update all of them.
795 if (SCI->ItinClassDef == ItinDef) {
796 ProcModel.ItinDefList[SCI->Index]= ItinData;
797 FoundClass = true;
798 }
799 }
800 if (!FoundClass) {
801 DEBUG(dbgs() <<ProcModel.ItinsDef->getName()
802 << " missing class foritinerary " << ItinDef->getName() << '\n');
803 }
804 }
805 // Check formissing itinerary entries.
806 assert(!ProcModel.ItinDefList[0]&& "NoItinerary class can't have rec");
807 DEBUG(
808 for(unsigned i = 1, N = ProcModel.ItinDefList.size(); i < N; ++i) {
809 if (!ProcModel.ItinDefList[i])
810 dbgs() <<ProcModel.ItinsDef->getName()
811 << " missingitinerary for class "
812 << SchedClasses[i].Name<< '\n';
813 });
814 }
815 }
3.5.1.5. ItinRW定义的处理
类似于InstRW,在执行步骤已经给出的情形下,ItinRW定义将一组InstrItinClass(即执行步骤)映射到一组SchedReadWrite。如果目标机器的描述给出了ItinRW的定义,就要通过下面的方法通过CodeGenProcModel实例将这些定义关联起来。
818 void CodeGenSchedModels::collectProcItinRW(){
819 RecVec ItinRWDefs =Records.getAllDerivedDefinitions("ItinRW");
820 std::sort(ItinRWDefs.begin(),ItinRWDefs.end(), LessRecord());
821 for (RecIterII = ItinRWDefs.begin(), IE = ItinRWDefs.end(); II != IE; ++II) {
822 if(!(*II)->getValueInit("SchedModel")->isComplete())
823 PrintFatalError((*II)->getLoc(),"SchedModel is undefined");
824 Record *ModelDef =(*II)->getValueAsDef("SchedModel");
825 ProcModelMapTy::const_iterator I =ProcModelMap.find(ModelDef);
826 if (I == ProcModelMap.end()) {
827 PrintFatalError((*II)->getLoc(),"Undefined SchedMachineModel "
828 + ModelDef->getName());
829 }
830 ProcModels[I->second].ItinRWDefs.push_back(*II);
831 }
832 }
容器ProcModelMap以描述处理器行程的Record对象为键值(可能是ProcessorItineraries派生定义,也可能是SchedMachineModel派生定义)。不过如果要使用ItinRW定义,Processor定义必须使用SchedMachineModel来描述处理器行程,因为当前处理器必须是使用SchedMachineModel才有必要使用ItinRW来映射已经存在的一组InstrItinClass。这里将ItinRW的Record对象记录在使用它的处理器的CodeGenProcModel对象的ItinRWDefs容器里。