LLVM学习笔记(37)

3.5.1.6.2. 从InstRW定义推导

InstRW为一组指令重新绑定SchedReadWrite。在前面CodeGenSchedModels的createInstRWClass方法里已经为InstRW定义准备了CodeGenSchedClass对象,并在CodeGenSchedClass对象的InstRWs容器里记录了该InstRW定义的Record实例。但对InstRW定义的处理并没完成,因为InstRW定义中的OperandReadWrites尚未处理,这些定义可能会衍生出一些新的调度类型,就像ItinRW的那样。

因此在inferSchedClasses的846行,如果当前CodeGenSchedClass对象的InstRWs容器不为空,表明这个对象至少对应一个InstRW定义。886行展开这些InstRW定义的Instrs成员,得到该InstRW定义所应用的指令组。然后在888行的循环里确定该InstRW定义与当前CodeGenSchedClass对象相关。

882      void CodeGenSchedModels::inferFromInstRWs(unsigned SCIdx) {

883        for (unsigned I = 0, E = SchedClasses[SCIdx].InstRWs.size(); I != E; ++I) {

884          assert(SchedClasses[SCIdx].InstRWs.size() == E && "InstrRWs was mutated!");

885          Record *Rec = SchedClasses[SCIdx].InstRWs[I];

886          const RecVec *InstDefs = Sets.expand(Rec);

887          RecIter II = InstDefs->begin(), IE = InstDefs->end();

888          for (; II != IE; ++II) {

889            if (InstrClassMap[*II] == SCIdx)

890              break;

891          }

892          // If this class no longer has any instructions mapped to it, it has become

893          // irrelevant.

894          if (II == IE)

895            continue;

896          IdxVec Writes, Reads;

897          findRWs(Rec->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads);

898          unsigned PIdx = getProcModel(Rec->getValueAsDef("SchedModel")).Index;

899          IdxVec ProcIndices(1, PIdx);

900          inferFromRW(Writes, Reads, SCIdx, ProcIndices); // May mutate SchedClasses.

901        }

902      }

接下来,类似于ItinRW,根据InstRW定义的OperandReadWrites里的内容来推导新的调度类型。

3.5.1.6.3. 从CodeGenSchedClass的Writes容器内容推导

我们知道一个CodeGenSchedClass对象由成员ItinClassDef,Writes与Reads的内容决定。

对于从ItinRW定义以及InstRW定义推导得到的CodeGenSchedClass对象,它们的ItinClassDef都是NULL,在容器Writes与Reads里,所有可能存在的SchedVariant定义都被展开了。

但对于从指令定义得到的CodeGenSchedClass对象,容器Writes与Reads的内容来自对应指令定义的SchedRW成员(类型list<SchedReadWrite>),这里面可能存在SchedVariant定义。

因此需要在inferSchedClasses的848行对它们调用一次inferFromRW。注意,参数ProcIndices来自SchedClasses[Idx].ProcIndices,这是适用该调度类型的处理器集合。因此,getIntersectingVariants的1095~1109行必须设计成那个样子。

​​​​​​​3.5.1.7. 保存资源对象

在构建了所有的调度类型CodeGenSchedClass实例后,CodeGenSchedModels构造函数最后调用CodeGenSchedModels::collectProcResources方法来保存调度类型需要资源的Record对象。

1433   void CodeGenSchedModels::collectProcResources() {

1434     // Add any subtarget-specific SchedReadWrites that are directly associated

1435     // with processor resources. Refer to the parent SchedClass's ProcIndices to

1436     // determine which processors they apply to.

1437     for (SchedClassIter SCI = schedClassBegin(), SCE = schedClassEnd();

1438          SCI != SCE; ++SCI) {

1439       if (SCI->ItinClassDef)

1440         collectItinProcResources(SCI->ItinClassDef);

1441       else {

1442         // This class may have a default ReadWrite list which can be overriden by

1443         // InstRW definitions.

1444         if (!SCI->InstRWs.empty()) {

1445           for (RecIter RWI = SCI->InstRWs.begin(), RWE = SCI->InstRWs.end();

1446                RWI != RWE; ++RWI) {

1447             Record *RWModelDef = (*RWI)->getValueAsDef("SchedModel");

1448             IdxVec ProcIndices(1, getProcModel(RWModelDef).Index);

1449             IdxVec Writes, Reads;

1450             findRWs((*RWI)->getValueAsListOfDefs("OperandReadWrites"),

1451                     Writes, Reads);

1452             collectRWResources(Writes, Reads, ProcIndices);

1453           }

1454         }

1455         collectRWResources(SCI->Writes, SCI->Reads, SCI->ProcIndices);

1456       }

1457     }

前面看过,CodeGenSchedClass对象的ItinClassDef如果不是NULL,那么这个ItinClassDef就是某类指令的执行步骤(itinerary,类型InstrItinClass)。而我们知道ItinRW定义将一组InstrItinClass定义映射到一组SchedReadWrite定义,那么我们需要下面的方法来处理这些ItinRW定义。

1531   void CodeGenSchedModels::collectItinProcResources(Record *ItinClassDef) {

1532     for (unsigned PIdx = 0, PEnd = ProcModels.size(); PIdx != PEnd; ++PIdx) {

1533       const CodeGenProcModel &PM = ProcModels[PIdx];

1534       // For all ItinRW entries.

1535       bool HasMatch = false;

1536       for (RecIter II = PM.ItinRWDefs.begin(), IE = PM.ItinRWDefs.end();

1537            II != IE; ++II) {

1538         RecVec Matched = (*II)->getValueAsListOfDefs("MatchedItinClasses");

1539         if (!std::count(Matched.begin(), Matched.end(), ItinClassDef))

1540           continue;

1541         if (HasMatch)

1542           PrintFatalError((*II)->getLoc(), "Duplicate itinerary class "

1543                           + ItinClassDef->getName()

1544                           + " in ItinResources for " + PM.ModelName);

1545         HasMatch = true;

1546         IdxVec Writes, Reads;

1547         findRWs((*II)->getValueAsListOfDefs("OperandReadWrites"), Writes, Reads);

1548         IdxVec ProcIndices(1, PIdx);

1549         collectRWResources(Writes, Reads, ProcIndices);

1550       }

1551     }

1552   }

首先,遍历处理器的定义查找与参数ItinClassDef相关的ItinRW定义,并获取处理器的索引。现在该处理器的资源由这些ItinRW的OperandReadWrites里的SchedReadWrite定义描述。

1593   void CodeGenSchedModels::collectRWResources(const IdxVec &Writes,

1594                                               const IdxVec &Reads,

1595                                               const IdxVec &ProcIndices) {

1596  

1597     for (IdxIter WI = Writes.begin(), WE = Writes.end(); WI != WE; ++WI)

1598       collectRWResources(*WI, /*IsRead=*/false, ProcIndices);

1599  

1600     for (IdxIter RI = Reads.begin(), RE = Reads.end(); RI != RE; ++RI)

1601       collectRWResources(*RI, /*IsRead=*/true, ProcIndices);

1602   }

为了关联资源,TableGen给出了若干特殊的SchedReadWrite派生定义。目标机器由此出发,可以模式自己资源的使用情况。我们需要解析这些特殊SchedReadWrite派生定义的特别语义。

1554   void CodeGenSchedModels::collectRWResources(unsigned RWIdx, bool IsRead,

1555                                               const IdxVec &ProcIndices) {

1556     const CodeGenSchedRW &SchedRW = getSchedRW(RWIdx, IsRead);

1557     if (SchedRW.TheDef) {

1558       if (!IsRead && SchedRW.TheDef->isSubClassOf("SchedWriteRes")) {

1559         for (IdxIter PI = ProcIndices.begin(), PE = ProcIndices.end();

1560              PI != PE; ++PI) {

1561           addWriteRes(SchedRW.TheDef, *PI);

1562         }

1563       }

1564       else if (IsRead && SchedRW.TheDef->isSubClassOf("SchedReadAdvance")) {

1565         for (IdxIter PI = ProcIndices.begin(), PE = ProcIndices.end();

1566              PI != PE; ++PI) {

1567           addReadAdvance(SchedRW.TheDef, *PI);

1568         }

1569       }

1570     }

1571     for (RecIter AI = SchedRW.Aliases.begin(), AE = SchedRW.Aliases.end();

1572          AI != AE; ++AI) {

1573       IdxVec AliasProcIndices;

1574       if ((*AI)->getValueInit("SchedModel")->isComplete()) {

1575         AliasProcIndices.push_back(

1576           getProcModel((*AI)->getValueAsDef("SchedModel")).Index);

1577       }

1578       else

1579         AliasProcIndices = ProcIndices;

1580       const CodeGenSchedRW &AliasRW = getSchedRW((*AI)->getValueAsDef("AliasRW"));

1581       assert(AliasRW.IsRead == IsRead && "cannot alias reads to writes");

1582  

1583       IdxVec ExpandedRWs;

1584       expandRWSequence(AliasRW.Index, ExpandedRWs, IsRead);

1585       for (IdxIter SI = ExpandedRWs.begin(), SE = ExpandedRWs.end();

1586            SI != SE; ++SI) {

1587         collectRWResources(*SI, IsRead, AliasProcIndices);

1588       }

1589     }

1590   }

首先,SchedWrite有一个特殊的派生类型——SchedWriteRes,它本身就会关联到一组资源,作用与WriteRes类似。CodeGenProcModel对象专门有记录WriteRes与SchedWriteRes的Record对象的容器WriteResDefs。

1674   void CodeGenSchedModels::addWriteRes(Record *ProcWriteResDef, unsigned PIdx) {

1675     assert(PIdx && "don't add resources to an invalid Processor model");

1676  

1677     RecVec &WRDefs = ProcModels[PIdx].WriteResDefs;

1678     RecIter WRI = std::find(WRDefs.begin(), WRDefs.end(), ProcWriteResDef);

1679     if (WRI != WRDefs.end())

1680       return;

1681     WRDefs.push_back(ProcWriteResDef);

1682  

1683     // Visit ProcResourceKinds referenced by the newly discovered WriteRes.

1684     RecVec ProcResDefs = ProcWriteResDef->getValueAsListOfDefs("ProcResources");

1685     for (RecIter WritePRI = ProcResDefs.begin(), WritePRE = ProcResDefs.end();

1686          WritePRI != WritePRE; ++WritePRI) {

1687       addProcResource(*WritePRI, ProcModels[PIdx]);

1688     }

1689   }

WriteRes与SchedWriteRes还派生自基类ProcWriteResources,具有类型为list<ProcResourceKind>的成员ProcResources。这就是WriteRes与SchedWriteRes所关联的资源,这些资源的定义需要被记录下来。

1651   void CodeGenSchedModels::addProcResource(Record *ProcResKind,

1652                                            CodeGenProcModel &PM) {

1653     for (;;) {

1654       Record *ProcResUnits = findProcResUnits(ProcResKind, PM);

1655  

1656       // See if this ProcResource is already associated with this processor.

1657       RecIter I = std::find(PM.ProcResourceDefs.begin(),

1658                             PM.ProcResourceDefs.end(), ProcResUnits);

1659       if (I != PM.ProcResourceDefs.end())

1660         return;

1661  

1662       PM.ProcResourceDefs.push_back(ProcResUnits);

1663       if (ProcResUnits->isSubClassOf("ProcResGroup"))

1664         return;

1665  

1666       if (!ProcResUnits->getValueInit("Super")->isComplete())

1667         return;

1668  

1669       ProcResKind = ProcResUnits->getValueAsDef("Super");

1670     }

1671   }

上面的参数ProcResKind可能是ProcResource派生定义,也可能是ProcResGroup派生定义。其中ProcResource派生自ProcResourceKind与ProcResourceUnits,而ProcResGroup只是ProcResourceKind的派生定义。因此,给定一个ProcResKind,如果它是ProcResourceUnits派生定义,我们就知道它是一个ProcResource。否则就是一个ProcResGroup。因此,下面1611行以下处理ProcResGroup定义。

1606   Record *CodeGenSchedModels::findProcResUnits(Record *ProcResKind,

1607                                                const CodeGenProcModel &PM) const {

1608    if (ProcResKind->isSubClassOf("ProcResourceUnits"))

1609       return ProcResKind;

1610  

1611     Record *ProcUnitDef = nullptr;

1612     RecVec ProcResourceDefs =

1613       Records.getAllDerivedDefinitions("ProcResourceUnits");

1614  

1615     for (RecIter RI = ProcResourceDefs.begin(), RE = ProcResourceDefs.end();

1616          RI != RE; ++RI) {

1617  

1618       if ((*RI)->getValueAsDef("Kind") == ProcResKind

1619           && (*RI)->getValueAsDef("SchedModel") == PM.ModelDef) {

1620         if (ProcUnitDef) {

1621           PrintFatalError((*RI)->getLoc(),

1622                           "Multiple ProcessorResourceUnits associated with "

1623                           + ProcResKind->getName());

1624         }

1625         ProcUnitDef = *RI;

1626      }

1627     }

1628     RecVec ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup");

1629     for (RecIter RI = ProcResGroups.begin(), RE = ProcResGroups.end();

1630          RI != RE; ++RI) {

1631  

1632       if (*RI == ProcResKind

1633           && (*RI)->getValueAsDef("SchedModel") == PM.ModelDef) {

1634         if (ProcUnitDef) {

1635           PrintFatalError((*RI)->getLoc(),

1636                           "Multiple ProcessorResourceUnits associated with "

1637                           + ProcResKind->getName());

1638         }

1639         ProcUnitDef = *RI;

1640       }

1641     }

1642     if (!ProcUnitDef) {

1643       PrintFatalError(ProcResKind->getLoc(),

1644                       "No ProcessorResources associated with "

1645                       + ProcResKind->getName());

1646     }

1647     return ProcUnitDef;

1648   }

目前ProcResourceUnits仅有的派生类是ProcResource,ProcResource向基类的Kind成员设置了一个特别的ProcResourceKind派生定义——EponymousProcResourceKind,因此ProcResGroup不会满足1618行的条件,1615行循环目前是没有作用的。对同一族处理器,类似的资源单位通常有相同的类型,而且这些资源单位的定义通常还是匿名的,因此ProcResourceUnits定义里专门给出了类型为SchedMachineModel的域SchedModel,用于区分不同的处理器。1632、1633行就是通过资源类型与作用处理器来找出对应的ProcResGroup定义。

CodeGenProcModel实例的容器ProcResourceDefs用于记录ProcResource与ProcResGroup派生定义的Record对象(因为在调用addProcResource 1662行push_back之前没有检查ProcResUnits的类型),而容器ProcResGroupDefs实际上并不使用。

在ProcResourceUnits定义里可以通过Super成员指定包含自己作为子集的更通用的资源单位,如果Super被定义了,就要辗转获取它的Record对象,直到某一级资源单位没有设置Super为止。

SchedRead也有一个特殊的派生类型——SchedReadAdvance,表示可以将关联的SchedWrite的时延降低指定周期。这些SchedReadAdvance必须记录下来。另外,还有一个ReadAdvance,它就像SchedReadAdvance的另一种封装。因此SchedReadAdvance与ReadAdvance将记录在一起。

1692   void CodeGenSchedModels::addReadAdvance(Record *ProcReadAdvanceDef,

1693                                           unsigned PIdx) {

1694     RecVec &RADefs = ProcModels[PIdx].ReadAdvanceDefs;

1695     RecIter I = std::find(RADefs.begin(), RADefs.end(), ProcReadAdvanceDef);

1696     if (I != RADefs.end())

1697       return;

1698     RADefs.push_back(ProcReadAdvanceDef);

1699   }

CodeGenProcModel对象的容器ReadAdvanceDefs就用于记录ReadAdvance定义的Record对象。在collectRWResources里对SchedReadWrite别名的处理也很熟悉,首先expandRWSequence展开可能存在的WriteSequence,然后对这些(展开后的)SchedReadWrite调用collectRWResources。

回到CodeGenSchedModels::collectProcResources,如果CodeGenSchedClass实例的ItinClassDef是空的,那么这个对象是根据ItinRW或instRW定义创建的。这些CodeGenSchedClass对象的InstRWs容器都是空的,因此1444~1454行实际上是不会执行的。因此,对从ItinRW与instRW定义衍生的调度类型,实际的处理由1455行的collectRWResources完成。

CodeGenSchedModels::collectProcResources(续)

1458     // Add resources separately defined by each subtarget.

1459     RecVec WRDefs = Records.getAllDerivedDefinitions("WriteRes");

1460     for (RecIter WRI = WRDefs.begin(), WRE = WRDefs.end(); WRI != WRE; ++WRI) {

1461       Record *ModelDef = (*WRI)->getValueAsDef("SchedModel");

1462       addWriteRes(*WRI, getProcModel(ModelDef).Index);

1463     }

1464     RecVec SWRDefs = Records.getAllDerivedDefinitions("SchedWriteRes");

1465     for (RecIter WRI = SWRDefs.begin(), WRE = SWRDefs.end(); WRI != WRE; ++WRI) {

1466       Record *ModelDef = (*WRI)->getValueAsDef("SchedModel");

1467       addWriteRes(*WRI, getProcModel(ModelDef).Index);

1468     }

1469     RecVec RADefs = Records.getAllDerivedDefinitions("ReadAdvance");

1470     for (RecIter RAI = RADefs.begin(), RAE = RADefs.end(); RAI != RAE; ++RAI) {

1471       Record *ModelDef = (*RAI)->getValueAsDef("SchedModel");

1472       addReadAdvance(*RAI, getProcModel(ModelDef).Index);

1473     }

1474     RecVec SRADefs = Records.getAllDerivedDefinitions("SchedReadAdvance");

1475     for (RecIter RAI = SRADefs.begin(), RAE = SRADefs.end(); RAI != RAE; ++RAI) {

1476       if ((*RAI)->getValueInit("SchedModel")->isComplete()) {

1477         Record *ModelDef = (*RAI)->getValueAsDef("SchedModel");

1478         addReadAdvance(*RAI, getProcModel(ModelDef).Index);

1479       }

1480     }

1481     // Add ProcResGroups that are defined within this processor model, which may

1482     // not be directly referenced but may directly specify a buffer size.

1483     RecVec ProcResGroups = Records.getAllDerivedDefinitions("ProcResGroup");

1484     for (RecIter RI = ProcResGroups.begin(), RE = ProcResGroups.end();

1485          RI != RE; ++RI) {

1486       if (!(*RI)->getValueInit("SchedModel")->isComplete())

1487         continue;

1488       CodeGenProcModel &PM = getProcModel((*RI)->getValueAsDef("SchedModel"));

1489       RecIter I = std::find(PM.ProcResourceDefs.begin(),

1490                             PM.ProcResourceDefs.end(), *RI);

1491       if (I == PM.ProcResourceDefs.end())

1492         PM.ProcResourceDefs.push_back(*RI);

1493     }

1494     // Finalize each ProcModel by sorting the record arrays.

1495     for (CodeGenProcModel &PM : ProcModels) {

1496       std::sort(PM.WriteResDefs.begin(), PM.WriteResDefs.end(),

1497                 LessRecord());

1498       std::sort(PM.ReadAdvanceDefs.begin(), PM.ReadAdvanceDefs.end(),

1499                 LessRecord());

1500       std::sort(PM.ProcResourceDefs.begin(), PM.ProcResourceDefs.end(),

1501                 LessRecord());

1502       DEBUG(

1503         PM.dump();

1504         dbgs() << "WriteResDefs: ";

1505         for (RecIter RI = PM.WriteResDefs.begin(),

1506                RE = PM.WriteResDefs.end(); RI != RE; ++RI) {

1507           if ((*RI)->isSubClassOf("WriteRes"))

1508             dbgs() << (*RI)->getValueAsDef("WriteType")->getName() << " ";

1509           else

1510             dbgs() << (*RI)->getName() << " ";

1511         }

1512         dbgs() << "\nReadAdvanceDefs: ";

1513         for (RecIter RI = PM.ReadAdvanceDefs.begin(),

1514                RE = PM.ReadAdvanceDefs.end(); RI != RE; ++RI) {

1515           if ((*RI)->isSubClassOf("ReadAdvance"))

1516             dbgs() << (*RI)->getValueAsDef("ReadType")->getName() << " ";

1517           else

1518             dbgs() << (*RI)->getName() << " ";

1519         }

1520         dbgs() << "\nProcResourceDefs: ";

1521         for (RecIter RI = PM.ProcResourceDefs.begin(),

1522                RE = PM.ProcResourceDefs.end(); RI != RE; ++RI) {

1523           dbgs() << (*RI)->getName() << " ";

1524         }

1525         dbgs() << '\n');

1526       verifyProcResourceGroups(PM);

1527     }

1528   }

collectProcResources接下来的处理属于补漏性质。将上面没有覆盖的WriteRes,SchedWriteRes,ReadAdvance,SchedReadAdvance及ProcResGroup保存到CodeGenSchedModels实例的相应容器中。最后对这些容器(WriteResDefs,ReadAdvanceDefs,ProcResourceDefs)按名字排序,并通过verifyProcResourceGroups方法确定重叠的组有共同的超集。

猜你喜欢

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