关于 add-ir-instruction
这项开发技能说明如何为 Hermes IR 添加新指令,面向编译器贡献者,涵盖所需文件、流水线改动与测试。
主要功能
- 01逐文件实现清单
- 02IR 文档约定
- 03验证器与类型推断更新
- 04代码生成与降级指导
- 05测试位置建议
适用场景
- 01规划 Hermes IR 新指令
- 02检查需要修改的编译器文件
- 03添加降级逻辑及流水线注册
- 04选择 IR 与代码生成测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ facebook/ hermes --skill add-ir-instruction安装后在 Claude Code 中输入/add-ir-instruction确认已加载。
详细介绍
向 Hermes 添加新的 IR 指令
添加新的 IR 指令时,必须修改一组特定文件。本指南介绍每个文件、应遵循的模式以及重要约定。
要修改的文件清单
doc/IR.md— 文档(唯一可以放置文档注释的地方)include/hermes/IR/Instrs.def— 指令注册include/hermes/IR/Instrs.h— 类定义(此处不要添加文档注释)include/hermes/IR/IRBuilder.h— Builder 声明lib/IR/IRBuilder.cpp— Builder 实现lib/IR/IRVerifier.cpp— 验证逻辑lib/Optimizer/Scalar/TypeInference.cpp— 类型推断占位实现lib/BCGen/HBC/ISel.cpp— HBC 指令选择(占位实现或实际实现)lib/BCGen/SH/SH.cpp— 静态 Hermes 代码生成(占位实现或实际实现)lib/BCGen/facebook/Mins/Mins.cpp— Mins 代码生成(占位实现或实际实现)- 测试 — 至少在
test/中更新或添加测试
如果该指令需要降低(即不能直接映射到字节码操作码),还需要修改:
include/hermes/BCGen/Lowering.h— 降低遍的声明lib/BCGen/Lowering.cpp— 降低遍的实现lib/BCGen/HBC/LoweringPipelines.cpp— 在 HBC 流水线中注册遍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会自动生成。