命令マッピングの使い方

はじめに

このドキュメントは、ターゲットに対する命令マッピングのサポートを追加する方法に関する情報を提供します。この機能の背後にある動機は、様々な最適化中に異なる命令形式を切り替える必要性から来ています。一アプローチとして、すべての命令とそれらが遷移できる形式をリストするswitch文を使用することができます。しかし、ハードコードされた命令名のため、メンテナンスのオーバーヘッドが大きくなります。また、.tdファイルに新しい命令が追加されるたびに、関連するすべてのswitch文を適切に変更する必要があります。代わりに、TableGenと.tdファイルからのいくらかのサポートを使用することで、メンテナンスコストを大幅に削減することができます。

InstrMapping クラスの概要

TableGenは、命令を相互にマッピングするためのリレーションシップモデルを使用します。これらのモデルは、InstrMappingクラスをベースとして記述されます。各モデルはInstrMappingクラスの様々なフィールドを設定し、そのモデルを使用してすべての命令を一意に記述できるようにします。TableGenはすべてのリレーションモデルを解析し、その情報を使用して、命令を相互に関連付けるリレーションテーブルを構築します。これらのテーブルは、それらを照会するための関数とともにXXXInstrInfo.incファイルに出力されます。以下は、Target.tdファイルで定義されているInstrMappingクラスの定義です。

class InstrMapping {
  // Used to reduce search space only to the instructions using this
  // relation model.
  string FilterClass;

  // List of fields/attributes that should be same for all the instructions in
  // a row of the relation table. Think of this as a set of properties shared
  // by all the instructions related by this relationship.
  list<string> RowFields = [];

  // List of fields/attributes that are same for all the instructions
  // in a column of the relation table.
  list<string> ColFields = [];

  // Values for the fields/attributes listed in 'ColFields' corresponding to
  // the key instruction. This is the instruction that will be transformed
  // using this relation model.
  list<string> KeyCol = [];

  // List of values for the fields/attributes listed in 'ColFields', one for
  // each column in the relation table. These are the instructions a key
  // instruction will be transformed into.
  list<list<string> > ValueCols = [];
}

サンプル例

非予測命令を受け取り、入力フラグinPredSenseに応じてその予測された真または偽の形式を返す関数int getPredOpcode(uint16_t Opcode, enum PredSense inPredSense)を作成したいとしましょう。このプロセスの最初のステップは、InstrMappingフィールドに適切な値を割り当てることにより、予測された命令を非予測形式に関連付けるリレーションシップモデルを定義することです。この関係では、インターフェース関数を照会するために使用されるため、非予測命令がキー命令として扱われます。

def getPredOpcode : InstrMapping {
  // Choose a FilterClass that is used as a base class for all the
  // instructions modeling this relationship. This is done to reduce the
  // search space only to these set of instructions.
  let FilterClass = "PredRel";

  // Instructions with same values for all the fields in RowFields form a
  // row in the resulting relation table.
  // For example, if we want to relate 'ADD' (non-predicated) with 'Add_pt'
  // (predicated true) and 'Add_pf' (predicated false), then all 3
  // instructions need to have same value for BaseOpcode field. It can be any
  // unique value (Ex: XYZ) and should not be shared with any other
  // instruction not related to 'add'.
  let RowFields = ["BaseOpcode"];

  // List of attributes that can be used to define key and column instructions
  // for a relation. Key instruction is passed as an argument
  // to the function used for querying relation tables. Column instructions
  // are the instructions they (key) can transform into.
  //
  // Here, we choose 'PredSense' as ColFields since this is the unique
  // attribute of the key (non-predicated) and column (true/false)
  // instructions involved in this relationship model.
  let ColFields = ["PredSense"];

  // The key column contains non-predicated instructions.
  let KeyCol = ["none"];

  // Two value columns - first column contains instructions with
  // PredSense=true while second column has instructions with PredSense=false.
  let ValueCols = [["true"], ["false"]];
}

TableGenは上記のリレーションシップモデルを使用して、非予測命令とその予測形式をマッピングするリレーションテーブルを出力します。また、テーブルを照会するためのインターフェース関数int getPredOpcode(uint16_t Opcode, enum PredSense inPredSense)も出力します。ここで、関数getPredOpcodeは、現在の命令のオペコードと目的の命令のPredSenseの2つの引数を取り、リレーションテーブルに見つかった場合、命令の予測形式を返します。命令をリレーションテーブルに追加するには、その定義に関連情報を含める必要があります。たとえば、ADD、ADD_pt(真)、ADD_pf(偽)命令の現在の定義を以下のようにします。

def ADD : ALU32_rr<(outs IntRegs:$dst), (ins IntRegs:$a, IntRegs:$b),
            "$dst = add($a, $b)",
            [(set (i32 IntRegs:$dst), (add (i32 IntRegs:$a),
                                           (i32 IntRegs:$b)))]>;

def ADD_Pt : ALU32_rr<(outs IntRegs:$dst),
                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
            "if ($p) $dst = add($a, $b)",
            []>;

def ADD_Pf : ALU32_rr<(outs IntRegs:$dst),
                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
            "if (!$p) $dst = add($a, $b)",
            []>;

このステップでは、これらの命令を修正して、リレーションシップモデルgetPredOpcodeに必要な情報を追加し、関連付けることができるようにします。

def ADD : PredRel, ALU32_rr<(outs IntRegs:$dst), (ins IntRegs:$a, IntRegs:$b),
            "$dst = add($a, $b)",
            [(set (i32 IntRegs:$dst), (add (i32 IntRegs:$a),
                                           (i32 IntRegs:$b)))]> {
  let BaseOpcode = "ADD";
  let PredSense = "none";
}

def ADD_Pt : PredRel, ALU32_rr<(outs IntRegs:$dst),
                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
            "if ($p) $dst = add($a, $b)",
            []> {
  let BaseOpcode = "ADD";
  let PredSense = "true";
}

def ADD_Pf : PredRel, ALU32_rr<(outs IntRegs:$dst),
                       (ins PredRegs:$p, IntRegs:$a, IntRegs:$b),
            "if (!$p) $dst = add($a, $b)",
            []> {
  let BaseOpcode = "ADD";
  let PredSense = "false";
}

上記のすべての命令はPredRelをベースクラスとして使用していることに注意してください。これは非常に重要です。なぜなら、TableGenはこれをgetPredOpcodeモデルの命令の選択のためのフィルターとして使用するためです。PredRelから派生していない命令は、解析から除外されます。BaseOpcodeも重要なフィールドです。モデルのRowFieldsとして選択されているため、関連付けるには3つの命令すべてで同じ値を持つ必要があります。次に、PredSenseは、その値をKeyColおよびValueColsと比較することで、列の位置を決定するために使用されます。命令がPredSenseの値をリレーションモデルで使用されていない値に設定した場合、リレーションテーブルには列が割り当てられません。