mcpField
A
add-ir-instruction

add-ir-instruction

github.com/facebook開発11.4k39 回インストール今日更新
リポジトリ

add-ir-instruction について

Hermes IR に命令を追加するための開発スキルです。コンパイラのコントリビューター向けに、必要な宣言、パイプライン変更、テストを案内します。

主な機能

  1. 01ファイル別の実装チェックリスト
  2. 02IR ドキュメントの規約
  3. 03検証と型推論の更新
  4. 04コード生成と低レベル化の案内
  5. 05テスト配置の指針

活用シーン

  1. 01Hermes IR の新命令の計画
  2. 02変更が必要なコンパイラファイルの確認
  3. 03低レベル化とパイプライン登録の追加
  4. 04IR とコード生成テストの選定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/facebook/hermes --skill add-ir-instruction

インストール後、Claude Code で/add-ir-instructionを入力して読み込まれたか確認します。

詳細

Hermes に新しい IR 命令を追加する

新しい IR 命令を追加する際は、特定のファイル一式を変更する必要があります。このガイドでは、各ファイルと従うべきパターン、重要な規約について説明します。

変更するファイルのチェックリスト

  1. doc/IR.md — ドキュメント(ドキュメントコメントを記述する唯一の場所)
  2. include/hermes/IR/Instrs.def — 命令の登録
  3. include/hermes/IR/Instrs.h — クラス定義(ここにドキュメントコメントを記述しないでください)
  4. include/hermes/IR/IRBuilder.h — ビルダーの宣言
  5. lib/IR/IRBuilder.cpp — ビルダーの実装
  6. lib/IR/IRVerifier.cpp — 検証ロジック
  7. lib/Optimizer/Scalar/TypeInference.cpp — 型推論のスタブ
  8. lib/BCGen/HBC/ISel.cpp — HBC 命令選択(スタブまたは実装)
  9. lib/BCGen/SH/SH.cpp — 静的 Hermes コード生成(スタブまたは実装)
  10. lib/BCGen/facebook/Mins/Mins.cpp — Mins コード生成(スタブまたは実装)
  11. テスト — 少なくとも test/ 内のテストを更新または追加します

命令にローワリングが必要な場合(つまり、バイトコードのオペコードに直接対応しない場合)は、次のファイルも必要です。

  1. include/hermes/BCGen/Lowering.h — ローワリングパスの宣言
  2. lib/BCGen/Lowering.cpp — ローワリングパスの実装
  3. lib/BCGen/HBC/LoweringPipelines.cpp — HBC パイプラインへのパスの登録
  4. lib/BCGen/SH/SH.cpp(lowerModuleIR 内)— SH パイプラインへのパスの登録

手順ガイド

1. doc/IR.md にドキュメントを記述する

このファイルが、命令のドキュメントを記述する唯一の場所です。 Instrs.h にドキュメントコメントを追加しないでください。

該当するセクションに Markdown の表の項目を追加します。

### MyNewInst

MyNewInst | _
--- | --- |
Description | Brief description of what the instruction does.
Example |   `MyNewInst %arg1, %arg2 : type`
Arguments | *%arg1* is ... *%arg2* is ...
Semantics | Describe the semantics, referencing the spec where appropriate.
Effects | Describe side effects (e.g., "May read and write memory.", "May read memory and throw.", "Does not read or write memory.").
2. Instrs.def に登録する

DEF_VALUE の項目を追加します。関連する命令の近くに配置してください。

DEF_VALUE(MyNewInst, Instruction)

別の命令のサブクラスである場合は、2 番目の引数に親クラスを指定します。ターミネーターの場合は、DEF_VALUE の代わりに TERMINATOR を使用します。

3. Instrs.h にクラスを定義する

このファイルにドキュメントコメントを追加しないでください。 ドキュメントは doc/IR.md に記述します。

次のパターンに正確に従ってください。

class MyNewInst : public Instruction {
  MyNewInst(const MyNewInst &) = delete;
  void operator=(const MyNewInst &) = delete;

 public:
  enum { Arg1Idx, Arg2Idx };
  explicit MyNewInst(Value *arg1, Value *arg2)
      : Instruction(ValueKind::MyNewInstKind) {
    // Optional assertions on operand types:
    // assert(arg2->getType().isSomeType() && "message");

    // Set the result type:
    setType(Type::createNoType());  // for instructions with no output
    // or: setType(Type::createFoo()); for typed instructions

    pushOperand(arg1);
    pushOperand(arg2);
  }

  explicit MyNewInst(
      const MyNewInst *src,
      llvh::ArrayRef<Value *> operands)
      : Instruction(src, operands) {}

  Value *getArg1() const {
    return getOperand(Arg1Idx);
  }
  Value *getArg2() const {
    return getOperand(Arg2Idx);
  }

  static bool hasOutput() {
    return false;  // true if the instruction produces a value
  }
  static bool isTyped() {
    return false;  // true if the output type is meaningful
  }

  SideEffect getSideEffectImpl() const {
    // Compose side effects. Common patterns:
    //   return {};                                          // pure
    //   return SideEffect{}.setReadHeap();                  // reads memory
    //   return SideEffect{}.setReadHeap().setWriteHeap();   // reads+writes
    //   return SideEffect{}.setThrow().setReadHeap();       // may throw + read
    return SideEffect{}.setThrow().setReadHeap();
  }

  static bool classof(const Value *V) {
    ValueKind kind = V->getKind();
    return kind == ValueKind::MyNewInstKind;
  }
};
4. IRBuilder.h に IRBuilder の宣言を追加する
MyNewInst *createMyNewInst(Value *arg1, Value *arg2);
5. IRBuilder.cpp に IRBuilder の実装を追加する
MyNewInst *IRBuilder::createMyNewInst(Value *arg1, Value *arg2) {
  auto *inst = new MyNewInst(arg1, arg2);
  insert(inst);
  return inst;
}
6. IRVerifier.cpp に検証を追加する

不変条件を確認する visit メソッドを追加します。

bool Verifier::visitMyNewInst(const MyNewInst &Inst) {
  AssertIWithMsg(
      Inst,
      Inst.getArg2()->getType().isSomeType(),
      "MyNewInst::Arg2 must be of SomeType");
  return true;
}
7. TypeInference.cpp に型推論を追加する

infer メソッドを追加します。出力のない命令では、createNoType() を返します。

Type inferMyNewInst(MyNewInst *inst) {
  return Type::createNoType();
}
8. コード生成スタブを追加する

コード生成の前に命令がローワリングされる場合は、致命的エラーを発生させるスタブを追加します。それ以外の場合は、実際のコード生成を実装します。

HBC ISel(lib/BCGen/HBC/ISel.cpp):

void HBCISel::generateMyNewInst(MyNewInst *Inst, BasicBlock *next) {
  hermes_fatal("MyNewInst should have been lowered.");
}

SH(lib/BCGen/SH/SH.cpp):

void generateMyNewInst(MyNewInst &inst) {
  hermes_fatal("MyNewInst should have been lowered");
}

Mins(lib/BCGen/facebook/Mins/Mins.cpp): 依頼がない限り、新しい命令の Mins コード生成は実装しないでください。スタブのままにしてください。

void generateMyNewInst(MyNewInst &inst) {
  unimplemented(inst);
}
9. (必要な場合)ローワリングパスを追加する

Lowering.h で宣言します。

/// Brief description of what the lowering does.
Pass *createLowerMyNewInst();

Lowering.cpp で実装します。

Pass *hermes::createLowerMyNewInst() {
  class ThisPass : public FunctionPass {
   public:
    explicit ThisPass() : FunctionPass("LowerMyNewInst") {}

    bool runOnFunction(Function *F) override {
      IRBuilder builder{F};
      bool changed = false;

      // Collect instructions first to avoid iterator invalidation.
      llvh::SmallVector<MyNewInst *, 4> insts;
      for (auto &BB : *F) {
        for (auto &I : BB) {
          if (auto *MNI = llvh::dyn_cast<MyNewInst>(&I))
            insts.push_back(MNI);
        }
      }

      for (auto *MNI : insts) {
        // Replace MNI with lowered IR...
        MNI->eraseFromParent();
        changed = true;
      }

      return changed;
    }
  };
  return new ThisPass();
}

パイプラインに登録します。

lib/BCGen/HBC/LoweringPipelines.cpp と lib/BCGen/SH/SH.cpp(lowerModuleIR)に、適切な位置で PM.addPass(createLowerMyNewInst()); を追加します(ローワリングによって導入される命令を処理する必要があるパスより前に追加してください)。

10. テストを追加または更新する

適切な test/ サブディレクトリに lit テストを追加します。IR の出力を検証するには、%FileCheck または %FileCheckOrRegen を使用してください。既存のテストで機能がカバーされている場合は、新しい命令を反映するよう期待される出力を更新します。

使用するサブディレクトリは、命令がコンパイラパイプラインとどのように連携するかによって異なります。すべての命令にすべてのディレクトリでのテストが必要なわけではありません。

  • test/IRGen/ — 命令が JavaScript ソースから直接生成される場合に使用します。ここでのテストでは、IRGen が期待される IR を生成することを検証します。
  • test/Optimizer/ — 命令が最適化パスに影響する場合、または最適化パスと連携する場合に使用します。
  • test/BCGen/ — 使用頻度は高くありませんが、この IR 命令が新しいバイトコード命令と組み合わせて使われる場合は、バイトコード生成のテストをここに配置します。

主な規約

  • Instrs.h にドキュメントコメントを書かないでください。 すべてのドキュメントは doc/IR.md に記載します。 Instrs.h 内のクラスには、その命令の動作を説明する /// や /** */ コメントを記述しないでください。副作用など、実装上の分かりにくい点を説明する簡潔なインラインコメントは問題ありません。
  • 配置が重要です。 すべてのファイルで、新しい命令は関連する命令の近くに配置してください (例:プライベートフィールドの命令はまとめて配置します)。
  • 命名を統一してください。 命令名(例:FooBarInst)は、すべてのファイルで統一する必要があります。対象は Instrs.def、Instrs.h、IRBuilder.h/cpp、IRVerifier.cpp、TypeInference.cpp、およびすべてのコード生成ファイルです。
  • ValueKind は自動的に生成されます。 Instrs.def に DEF_VALUE(MyNewInst, Instruction) を追加すると、列挙値 ValueKind::MyNewInstKind が自動的に生成されます。

コメント