GCC插件能安全操作RTL的阶段是“RTL expansion之后、machine-dependent optimization之前”,须通过PLUGIN_PASS_MANAGER_SETUP注册自定义RTL pass,指定reference_pass_name(如"final"),并使用struct rtl_opt_pass在预定义hook点挂载,不可在PLUGIN_START_UNIT等早期时机访问。

什么是 GCC 插件能碰的 RTL 阶段
GCC 插件机制本身不直接暴露 RTL(Register Transfer Language)树的稳定 API;你看到的 rtl 相关结构体(如 rtx、insn)在插件中**不能安全访问或修改**,除非你明确知道当前 pass 的 RTL 已生成且未被后续 pass 破坏。真正可介入的时机是 GCC 的 **“RTL expansion” 之后、“machine-dependent optimization” 之前**,典型入口是 PLUGIN_PASS_MANAGER_SETUP 注册的自定义 RTL pass,或监听 PLUGIN_FINISH_UNIT 后手动遍历函数的 fun->curr_insn 链——但后者极易因 RTL 未完全合法而崩溃。
常见误判:以为注册 PLUGIN_START_UNIT 就能拿到 RTL,其实这时只有 GIMPLE;等不到 RTL 出现就提前返回。
怎么注册一个能操作 RTL 的插件 pass
必须用 struct register_pass_info 显式插入到 RTL 流水线中,且 reference_pass_name 要指定为已存在的 RTL pass 名(如 "final" 或 "split1"),不能随便写。GCC 不允许插件在任意位置插入 RTL pass,只接受在预定义 hook 点附近挂载。
-
pass字段需指向自定义struct rtl_opt_pass实例,其gate回调必须返回 true 才会执行,否则整个 pass 被跳过 -
ref_pass_instance_number设为 0 表示插在 reference pass 之前;设为 1 表示之后(但某些 RTL pass 不支持后置) - 必须在
plugin_init中调用register_callback注册PLUGIN_PASS_MANAGER_SETUP,否则你的 pass 根本不会被 GCC 认出来
示例关键片段:
立即学习“C++免费学习笔记(深入)”;
static struct rtl_opt_pass pass_my_rtl_pass = {
{"my_rtl_pass", /* name */
OPTGROUP_NONE, /* optinfo_flags */
NULL, /* gate */
my_rtl_execute, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
TV_NONE, /* tv_id */
PROP_rtl, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func | TODO_verify_rtl_sharing /* todo_flags_finish */
}
};
<p>static const struct pass_data pass_data_my_rtl_pass = {
"my_rtl_pass", /<em> name </em>/
OPTGROUP_NONE,
TV_NONE,
PROP_rtl,
0,
0,
0,
0,
0,
0
};</p>修改 RTL 指令时最容易 crash 的三个原因
RTL 是高度状态敏感的数据结构,GCC 内部大量依赖 INSN_UID、REG_NOTES、DF(dataflow)信息的一致性。直接改 PATTERN (insn) 或删 insn 而不通知 DF,下个优化 pass 很可能读到脏数据然后 abort。
- 没调
df_insn_rescan (insn)—— 修改指令内容后必须重扫描,否则寄存器生命周期计算错误 - 用
delete_insn (insn)删除指令但没调delete_insn_and_stuff (insn)—— 导致REG_NOTES和 jump table 引用悬空 - 在
my_rtl_execute里遍历get_insns()链表并边遍历边删/插,没用next_insn缓存下一个节点 —— 链表指针失效,直接 segfault
安全做法:先收集要改的 rtx 指针到 vector,再倒序处理;所有修改后立即调 df_insn_rescan。
为什么调试 RTL 插件比 GIMPLE 插件痛苦得多
因为 RTL 没有统一的 dump 接口。debug_rtx (x) 只能在 GDB 里看单个 rtx,无法像 dump_file 那样输出结构化日志;print_rtl 输出的是原始 Lisp 风格文本,且默认不带行号和上下文。更麻烦的是:GCC 编译时若开启 -O2,RTL 会被快速重写,你插的 pass 可能运行在 split 后、reload 前、或 machine reorg 之后——每个阶段 RTL 形态完全不同,同一段代码在不同优化级下行为不一致。
建议强制用 -O0 -fno-omit-frame-pointer -g 启动测试,配合 gcc -fdump-rtl-all 查看各阶段 RTL 文件名,再用 grep -A5 -B5 "your_label" *.rtl 定位目标 insn 的实际形态。别信文档里写的 “RTL is stable”——它只对 GCC 开发者稳定,对插件作者是灰盒。


















