LLVM学习笔记(35)

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容器里。


猜你喜欢

转载自blog.csdn.net/wuhui_gdnt/article/details/80771882