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. 01规划 Hermes IR 新指令
  2. 02检查需要修改的编译器文件
  3. 03添加降级逻辑及流水线注册
  4. 04选择 IR 与代码生成测试

安装

复制下面的命令,在终端中运行。

$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 — Builder 声明
  5. lib/IR/IRBuilder.cpp — Builder 实现
  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)

如果它是另一条指令的子类,则将父类作为第二个参数。如果它是终结符,则使用 TERMINATOR,而不是 DEF_VALUE。

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 测试。使用 %FileCheck 或 %FileCheckOrRegen 验证 IR 输出。如果现有测试已覆盖此功能,则更新其预期输出,以反映新增指令。

应使用哪个子目录取决于该指令与编译器流水线的交互方式;并非每条指令都需要在每个目录中添加测试:

  • 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 会自动生成。

评论

支持 Markdown