FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

オリジナルLLVMバックエンド実装をまとめる(19. LLVM IRからInstruction Selectionまでの流れ2)

前回の続き。

Legalizeでは、ターゲット固有のノードへの変換を行う。LLVM IRでは様々な演算ノードを定義しているが、ターゲットアーキテクチャによってはサポートしていない演算もある。

このような演算に関しては、ターゲットが生成できるノードに変換するなどの指示を出す必要がある。

これらについては、後述するMYRSCVXTargetLoweringで指示を出すが、例えば、ローテート命令はRISC-Vではそのまま生成できない。したがって、以下のように記述することでローテート演算の生成を抑制する。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
  setOperationAction(ISD::ROTL, XLenVT, Expand);
  setOperationAction(ISD::ROTR, XLenVT, Expand);

setOperationActionにより、当該ノードの動作を決定する。上記の例だと、ISD::ROTL演算の`XLenVT‘サイズの演算について、Expandのアクションを設定する。

  • llvm-myriscvx80/include/llvm/CodeGen/TargetLowering.h
  /// Indicate that the specified operation does not work with the specified
  /// type and indicate what to do about it. Note that VT may refer to either
  /// the type of a result or that of an operand of Op.
  void setOperationAction(unsigned Op, MVT VT,
                          LegalizeAction Action) {
    assert(Op < array_lengthof(OpActions[0]) && "Table isn't big enough!");
    OpActions[(unsigned)VT.SimpleTy][Op] = Action;
  }

上記の場合だとノードのアクションがExpandに設定されるのだが、Legalizeの実行時にSelectionDAGLegalize::LegalizeOpが呼ばれ判断が行われる。 Expandの場合は、ExpandNode()が呼び出され、さらにExpandROTにより、より単純なノードへの置き換えが行われる。

  • llvm-myriscvx80/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
/// Return a legal replacement for the given operation, with all legal operands.
void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
  LLVM_DEBUG(dbgs() << "\nLegalizing: "; Node->dump(&DAG));
...
    switch (Action) {
    case TargetLowering::Legal:
      LLVM_DEBUG(dbgs() << "Legal node: nothing to do\n");
...
    case TargetLowering::Expand:
      if (ExpandNode(Node))
        return;
      LLVM_FALLTHROUGH;
...
  • llvm-myriscvx80/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
  LLVM_DEBUG(dbgs() << "Trying to expand node\n");
  SmallVector<SDValue, 8> Results;
...
  case ISD::ROTL:
  case ISD::ROTR:
    if (TLI.expandROT(Node, Tmp1, DAG))
      Results.push_back(Tmp1);
    break;
...

最終的に、シフト演算と論理演算命令に置き換わる。

  • llvm-myriscvx80/lib/CodeGen/SelectionDAG/TargetLowering.cpp
// TODO: Merge with expandFunnelShift.
bool TargetLowering::expandROT(SDNode *Node, SDValue &Result,
                               SelectionDAG &DAG) const {
  EVT VT = Node->getValueType(0);
  unsigned EltSizeInBits = VT.getScalarSizeInBits();
...
  // Otherwise,
  //   (rotl x, c) -> (or (shl x, (and c, w-1)), (srl x, (and w-c, w-1)))
  //   (rotr x, c) -> (or (srl x, (and c, w-1)), (shl x, (and w-c, w-1)))
  //
  assert(isPowerOf2_32(EltSizeInBits) && EltSizeInBits > 1 &&
         "Expecting the type bitwidth to be a power of 2");
  unsigned ShOpc = IsLeft ? ISD::SHL : ISD::SRL;
  unsigned HsOpc = IsLeft ? ISD::SRL : ISD::SHL;
  SDValue BitWidthMinusOneC = DAG.getConstant(EltSizeInBits - 1, DL, ShVT);
  SDValue NegOp1 = DAG.getNode(ISD::SUB, DL, ShVT, BitWidthC, Op1);
  SDValue And0 = DAG.getNode(ISD::AND, DL, ShVT, Op1, BitWidthMinusOneC);
  SDValue And1 = DAG.getNode(ISD::AND, DL, ShVT, NegOp1, BitWidthMinusOneC);
  Result = DAG.getNode(ISD::OR, DL, VT, DAG.getNode(ShOpc, DL, VT, Op0, And0),
                       DAG.getNode(HsOpc, DL, VT, Op0, And1));
  return true;
} 

命令のSelection

ここまで来るとほぼ一対一でSelectionDAGのノードを命令に変換することができる。 次は、SelectionDAGのノードを命令に相当するノードMI(Machine Instruction)に変換する。 これには、DAGToDAGISelによって処理される。 今回作成するMYRISCVXアーキテクチャではMYRISCVXDAGToDAGISelによって行われる。 MYRISCVXDAGToDAGISel::Select()によって行われる。

//@Select {
/// Select instructions not customized! Used for
/// expanded, promoted and normal instructions
void MYRISCVXDAGToDAGISel::Select(SDNode *Node) {
  //@Select }
...
  // Select the default instruction
  SelectCode(Node);
}

SelectCodeMYRISCVXGenDAGISel.incに定義されており、TableGenにより生成されたノードから命令ノードへの変換が行われる。

  • build-myriscvx80/lib/Target/MYRISCVX/MYRISCVXGenDAGISel.inc
#if defined(GET_DAGISEL_BODY) || DAGISEL_INLINE
void DAGISEL_CLASS_COLONCOLON SelectCode(SDNode *N)
{
  // Some target values are emitted as 2 bytes, TARGET_VAL handles
...

これにより、以下のようなDAGが生成される。MYRISCVXISD::RetRetRAに変換されている。

f:id:msyksphinz:20191011015229p:plain:w300
Instruction Selectionにより生成されたDAG