[PATCH 1/2] TableGen: Generate a function for getting operand indices based on their defined names

Tom Stellard tom at stellard.net
Mon Jun 24 12:22:52 PDT 2013


Ping.

On Thu, Jun 20, 2013 at 10:58:03AM -0700, Tom Stellard wrote:
> Hi Sean,
> 
> Attached is an updated patch which addresses your comments.
> 
> On Wed, Jun 19, 2013 at 12:52:03PM -0700, Sean Silva wrote:
> > +.. code-block:: llvm
> > 
> > This should be a `c++` code block, since this is c++ code (this determines
> > how it is syntax-highlighted).
> > 
> > +  int DIndex = SP::getNamedOperandIdx(SP::XNORrr, SP::OpName::d);     //
> > => -1
> > 
> > Does it even make sense to return -1 for "not found"?
> > 
> 
> Do you think we should be returning some other value for "not found"?
> 
> -Tom
> 
> > +///   for looking up the operand index for an instruction, given a vlue
> > from
> > 
> > Spelling: vlue
> > 
> > +    std::map<unsigned, unsigned> OpList = i->first;
> > 
> > Do you really need to make a copy here?
> > 
> > +    std::map<unsigned, unsigned> OpList = i->first;
> > +    std::vector<std::string> OpcodeList = i->second;
> > 
> > Same.
> > 
> > +  for (std::map<std::string, unsigned>::iterator i = Operands.begin(),
> > +                                             e = Operands.end();
> > +                                             i != e; ++i) {
> > +    OS << "  " << i->first << " = " << i->second << ",\n";
> > +  }
> > 
> > The formatting of this looks really awkward. Regardless, you can make this
> > more readable with a typedef:
> > 
> > typedef std::map<std::string, unsigned>::iterator Iter;
> > for (Iter i = Operands.begin(), e = Operands.end(); i != e; ++i)
> >   OS << "  " << i->first << " = " << i->second << ",\n";
> > 
> > +    std::map<unsigned, unsigned> OpList;
> > +    for (unsigned j = 0, je = Inst->Operands.size(); j != je; ++j) {
> > +      CGIOperandList::OperandInfo Info = Inst->Operands[j];
> > +      std::string Name =  Info.Name;
> > +      if (Operands.count(Name) == 0) {
> > +        Operands[Name] = NumOperands++;
> > +      }
> > +      unsigned OperandId = Operands[Name];
> > +      OpList[OperandId] = Info.MIOperandNo;
> > +    }
> > 
> > Do you really need to make a copy of Name? Also, Info is really not cheap
> > to copy (at least 4 std::string and a vector<bool>). Unnecessary copies
> > seems to be a recurring problem in this patch; please go through the entire
> > patch and ensure that you are not unnecessarily making copies.
> > 
> > Also, You're doing 2-3 separate map lookups for the same element in the
> > inner loop here (are we smart enough to optimize that away? I would be
> > surprised if we were).
> > 
> > +  for (unsigned i = 0, e = NumberedInstructions.size(), NumOperands = 0;
> > +                                                                  i != e;
> > ++i) {
> > +    const CodeGenInstruction *Inst = NumberedInstructions[i];
> > +    if (!Inst->TheDef->getValueAsBit("UseNamedOperandTable")) {
> > +      continue;
> > +    }
> > +    std::map<unsigned, unsigned> OpList;
> > +    for (unsigned j = 0, je = Inst->Operands.size(); j != je; ++j) {
> > +      CGIOperandList::OperandInfo Info = Inst->Operands[j];
> > +      std::string Name =  Info.Name;
> > +      if (Operands.count(Name) == 0) {
> > +        Operands[Name] = NumOperands++;
> > +      }
> > +      unsigned OperandId = Operands[Name];
> > +      OpList[OperandId] = Info.MIOperandNo;
> > +    }
> > +    OperandMap[OpList].push_back(Namespace + "::" +
> > Inst->TheDef->getName());
> > +  }
> > 
> > Can you move this into a helper function?
> > 
> > +  unsigned TableIndex = 0;
> > +  for (OpNameMapTy::iterator i = OperandMap.begin(), e = OperandMap.end();
> > +                                                     i != e; ++i,
> > ++TableIndex) {
> > +    std::map<unsigned, unsigned> OpList = i->first;
> > +    std::vector<std::string> OpcodeList = i->second;
> > +
> > +    for (unsigned ii = 0, ie = OpcodeList.size(); ii != ie; ++ii)
> > +      OS << "  case " << OpcodeList[ii] << ":\n";
> > +
> > +    OS << "    return OperandMap[" << TableIndex << "][NamedIdx];\n";
> > +  }
> > 
> > The ++TableIndex is kind of hidden in the loop header, can you just do
> > TableIndex++ as you output it?
> > 
> > -- Sean Silva

> From 7e7ee5ee45aee0793c6f27cab7910645063454b9 Mon Sep 17 00:00:00 2001
> From: Tom Stellard <thomas.stellard at amd.com>
> Date: Thu, 30 May 2013 10:10:50 -0700
> Subject: [PATCH] TableGen: Generate a function for getting operand indices
>  based on their defined names v5
> 
> This patch modifies TableGen to generate a function in
> ${TARGET}GenInstrInfo.inc called getNamedOperandIdx(), which can be used
> to look up indices for operands based on their names.
> 
> In order to activate this feature for an instruction, you must set the
> UseNamedOperandTable bit.
> 
> For example, if you have an instruction like:
> 
> def ADD : TargetInstr <(outs GPR:$dst), (ins GPR:$src0, GPR:$src1)>;
> 
> You can look up the operand indices using the new function, like this:
> 
> Target::getNamedOperandIdx(Target::ADD, Target::OpName::dst)  => 0
> Target::getNamedOperandIdx(Target::ADD, Target::OpName::src0) => 1
> Target::getNamedOperandIdx(Target::ADD, Target::OpName::src1) => 2
> 
> The operand names are case sensitive, so $dst and $DST are considered
> different operands.
> 
> This change is useful for R600 which has instructions with a large number
> of operands, many of which model single bit instruction configuration
> values.  These configuration bits are common across most instructions,
> but may have a different operand index depending on the instruction type.
> It is useful to have a convenient way to look up the operand indices,
> so these bits can be generically set on any instruction.
> 
> v2:
>   - Don't uppercase enum values
>   - Use table compresion to reduce function size
> 
> v3:
>   - Only generate table for instructions with the UseNamedOperandTable
>     bit set.
> 
> v4:
>   - Clean up code and add documentation
> 
> v5:
>   - More code cleanups
> ---
>  docs/WritingAnLLVMBackend.rst       |  41 ++++++++++++
>  include/llvm/Target/Target.td       |   5 ++
>  utils/TableGen/InstrInfoEmitter.cpp | 130 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 176 insertions(+)
> 
> diff --git a/docs/WritingAnLLVMBackend.rst b/docs/WritingAnLLVMBackend.rst
> index a03a5e4..9e3cb9e 100644
> --- a/docs/WritingAnLLVMBackend.rst
> +++ b/docs/WritingAnLLVMBackend.rst
> @@ -911,6 +911,47 @@ format instructions will bind the operands to the ``rd``, ``rs1``, and ``rs2``
>  fields.  This results in the ``XNORrr`` instruction binding ``$dst``, ``$b``,
>  and ``$c`` operands to the ``rd``, ``rs1``, and ``rs2`` fields respectively.
>  
> +TableGen will also generate a function called getNamedOperandIdx() which
> +can be used to look up an operand's index in a MachineInstr based on its
> +TableGen name.  Setting the UseNamedOperandTable bit in an instruction's
> +TableGen definition will add all of its operands to an enumeration in the
> +llvm::XXX:OpName namespace and also add an entry for it into the OperandMap
> +table, which can be queried using getNamedOperandIdx()
> +
> +.. code-block:: llvm
> +
> +  int DstIndex = SP::getNamedOperandIdx(SP::XNORrr, SP::OpName::dst); // => 0
> +  int BIndex = SP::getNamedOperandIdx(SP::XNORrr, SP::OpName::b);     // => 1
> +  int CIndex = SP::getNamedOperandIdx(SP::XNORrr, SP::OpName::c);     // => 2
> +  int DIndex = SP::getNamedOperandIdx(SP::XNORrr, SP::OpName::d);     // => -1
> +
> +  ...
> +
> +The entries in the OpName enum are taken verbatim from the TableGen definitions,
> +so operands with lowercase names will have lower case entries in the enum.
> +
> +To include the getNamedOperandIdx() function in your backend, you will need
> +to define a few preprocessor macros in XXXInstrInfo.cpp and XXXInstrInfo.h.
> +For example:
> +
> +XXXInstrInfo.cpp:
> +
> +.. code-block:: c++ 
> +
> +  #define GET_INSTRINFO_NAMED_OPS // For getNamedOperandIdx() function
> +  #include "XXXGenInstrInfo.inc"
> +
> +XXXInstrInfo.h:
> +
> +.. code-block:: c++
> +
> +  #define GET_INSTRINFO_OPERAND_ENUM // For OpName enum
> +  #include "XXXGenInstrInfo.inc"
> +
> +  namespace XXX {
> +    int16_t getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIndex);
> +  } // End namespace XXX
> +
>  Instruction Relation Mapping
>  ----------------------------
>  
> diff --git a/include/llvm/Target/Target.td b/include/llvm/Target/Target.td
> index a9644d4..97df64c 100644
> --- a/include/llvm/Target/Target.td
> +++ b/include/llvm/Target/Target.td
> @@ -445,6 +445,11 @@ class Instruction {
>    string TwoOperandAliasConstraint = "";
>  
>    ///@}
> +
> +  /// UseNamedOperandTable - If set, the operand indices of this instruction
> +  /// can be queried via the getNamedOperandIdx() function which is generated
> +  /// by TableGen.
> +  bit UseNamedOperandTable = 0;
>  }
>  
>  /// PseudoInstExpansion - Expansion information for a pseudo-instruction.
> diff --git a/utils/TableGen/InstrInfoEmitter.cpp b/utils/TableGen/InstrInfoEmitter.cpp
> index d6020a8..532e206 100644
> --- a/utils/TableGen/InstrInfoEmitter.cpp
> +++ b/utils/TableGen/InstrInfoEmitter.cpp
> @@ -45,11 +45,25 @@ private:
>    void emitEnums(raw_ostream &OS);
>  
>    typedef std::map<std::vector<std::string>, unsigned> OperandInfoMapTy;
> +
> +  /// The keys of this map are maps which have OpName enum values as their keys
> +  /// and instruction operand indices as their values.  The values of this map
> +  /// are lists of instruction names.
> +  typedef std::map<std::map<unsigned, unsigned>,
> +                   std::vector<std::string> > OpNameMapTy;
> +  typedef std::map<std::string, unsigned>::iterator StrUintMapIter;
>    void emitRecord(const CodeGenInstruction &Inst, unsigned Num,
>                    Record *InstrInfo,
>                    std::map<std::vector<Record*>, unsigned> &EL,
>                    const OperandInfoMapTy &OpInfo,
>                    raw_ostream &OS);
> +  void initOperandMapData(
> +             const std::vector<const CodeGenInstruction *> NumberedInstructions,
> +             std::string Namespace,
> +             std::map<std::string, unsigned> &Operands,
> +             OpNameMapTy &OperandMap);
> +  void emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target,
> +            const std::vector<const CodeGenInstruction*> &NumberedInstructions);
>  
>    // Operand information.
>    void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs);
> @@ -176,6 +190,120 @@ void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS,
>    }
>  }
>  
> +
> +/// Initialize data structures for generating operand name mappings.
> +/// 
> +/// \p Operands [out] A map used to generate the OpName enum with operand
> +///     names as its keys and operand enum values as its values.
> +///  \p OperandMap [out] A map for representing the operand name mappings for
> +///     each instructions.  This is used to generate the OperandMap table as
> +///     well as the getNamedOperandIdx() function.
> +void InstrInfoEmitter::initOperandMapData(
> +        const std::vector<const CodeGenInstruction *> NumberedInstructions,
> +        std::string Namespace,
> +        std::map<std::string, unsigned> &Operands,
> +        OpNameMapTy &OperandMap) {
> +
> +  for (unsigned i = 0, e = NumberedInstructions.size(), NumOperands = 0;
> +                                                                  i != e; ++i) {
> +    const CodeGenInstruction *Inst = NumberedInstructions[i];
> +    if (!Inst->TheDef->getValueAsBit("UseNamedOperandTable")) {
> +      continue;
> +    }
> +    std::map<unsigned, unsigned> OpList;
> +    for (unsigned j = 0, je = Inst->Operands.size(); j != je; ++j) {
> +      const CGIOperandList::OperandInfo &Info = Inst->Operands[j];
> +      StrUintMapIter I = Operands.find(Info.Name);
> +
> +      if (I == Operands.end()) {
> +        I = Operands.insert(Operands.begin(),
> +                    std::pair<std::string, unsigned>(Info.Name, NumOperands++));
> +      }
> +      OpList[I->second] = Info.MIOperandNo;
> +    }
> +    OperandMap[OpList].push_back(Namespace + "::" + Inst->TheDef->getName());
> +  }
> +}
> +
> +/// Generate a table and function for looking up the indices of operands by
> +/// name.
> +///
> +/// This code generates:
> +/// - An enum in the llvm::TargetNamespace::OpName namespace, with one entry
> +///   for each operand name.
> +/// - A 2-dimensional table called OperandMap for mapping OpName enum values to
> +///   operand indices.
> +/// - A function called getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx)
> +///   for looking up the operand index for an instruction, given a value from
> +///   OpName enum
> +void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS,
> +           const CodeGenTarget &Target,
> +           const std::vector<const CodeGenInstruction*> &NumberedInstructions) {
> +
> +  std::string Namespace = Target.getInstNamespace();
> +  std::string OpNameNS = "OpName";
> +  // Map of operand names to their enumeration value.  This will be used to
> +  // generate the OpName enum.
> +  std::map<std::string, unsigned> Operands;
> +  OpNameMapTy OperandMap;
> +
> +  initOperandMapData(NumberedInstructions, Namespace, Operands, OperandMap);
> +
> +  OS << "#ifdef GET_INSTRINFO_OPERAND_ENUM\n";
> +  OS << "#undef GET_INSTRINFO_OPERAND_ENUM\n";
> +  OS << "namespace llvm {";
> +  OS << "namespace " << Namespace << " {\n";
> +  OS << "namespace " << OpNameNS << " { \n";
> +  OS << "enum {\n";
> +  for (StrUintMapIter i = Operands.begin(), e = Operands.end(); i != e; ++i)
> +    OS << "  " << i->first << " = " << i->second << ",\n";
> +
> +  OS << "OPERAND_LAST";
> +  OS << "\n};\n";
> +  OS << "} // End namespace OpName\n";
> +  OS << "} // End namespace " << Namespace << "\n";
> +  OS << "} // End namespace llvm\n";
> +  OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n";
> +
> +  OS << "#ifdef GET_INSTRINFO_NAMED_OPS\n";
> +  OS << "#undef GET_INSTRINFO_NAMED_OPS\n";
> +  OS << "namespace llvm {";
> +  OS << "namespace " << Namespace << " {\n";
> +  OS << "int16_t getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx) {\n";
> +  OS << "  static const int16_t OperandMap []["<< Operands.size() << "] = {\n";
> +  for (OpNameMapTy::iterator i = OperandMap.begin(), e = OperandMap.end();
> +                                                     i != e; ++i) {
> +    const std::map<unsigned, unsigned> &OpList = i->first;
> +    OS << "{";
> +
> +    // Emit a row of the OperandMap table
> +    for (unsigned i = 0, e = Operands.size(); i != e; ++i)
> +      OS << (OpList.count(i) == 0 ? -1 : (int)OpList.find(i)->second) << ", ";
> +
> +    OS << "},\n";
> +  }
> +  OS << "};\n";
> +
> +  OS << "  switch(Opcode) {\n";
> +  unsigned TableIndex = 0;
> +  for (OpNameMapTy::iterator i = OperandMap.begin(), e = OperandMap.end();
> +                                                     i != e; ++i) {
> +    std::vector<std::string> &OpcodeList = i->second;
> +
> +    for (unsigned ii = 0, ie = OpcodeList.size(); ii != ie; ++ii)
> +      OS << "  case " << OpcodeList[ii] << ":\n";
> +
> +    OS << "    return OperandMap[" << TableIndex++ << "][NamedIdx];\n";
> +  }
> +  OS << "    default: return -1;\n";
> +  OS << "  }\n";
> +  OS << "}\n";
> +  OS << "} // End namespace " << Namespace << "\n";
> +  OS << "} // End namespace llvm\n";
> +  OS << "#endif //GET_INSTRINFO_NAMED_OPS\n";
> +
> +}
> +
>  //===----------------------------------------------------------------------===//
>  // Main Output.
>  //===----------------------------------------------------------------------===//
> @@ -293,6 +421,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
>    OS << "} // End llvm namespace \n";
>  
>    OS << "#endif // GET_INSTRINFO_CTOR\n\n";
> +
> +  emitOperandNameMappings(OS, Target, NumberedInstructions);
>  }
>  
>  void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
> -- 
> 1.7.11.4
> 

> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits




More information about the llvm-commits mailing list