在GCC 10.3.0插件中实现自定义#pragma标记代码的技术问询
GCC 10.3.0插件实现自定义#pragma及GIMPLE阶段处理方案
核心思路
通过注册多个GCC插件事件回调,分三步实现需求:
- 解析自定义
#pragma my_pragma,设置标记开关 - 在树阶段捕获函数/if语句,将符合条件的节点存入全局哈希表,完成标记传递
- 注册自定义GIMPLE PASS,基于哈希表中的标记对GIMPLE代码做针对性处理
1. 自定义#pragma解析与节点标记
关键事件与回调
- PLUGIN_PRAGMA:注册自定义pragma的解析逻辑,触发标记开关
- PLUGIN_FINISH_DECL:捕获函数定义完成事件,标记#pragma后的函数
- PLUGIN_PRE_GENERICIZE:在生成GIMPLE前遍历函数树结构,标记#pragma后的if语句
代码实现
#include "gcc-plugin.h" #include "plugin-version.h" #include "tree.h" #include "c-family/c-pragma.h" #include "context.h" #include "pass_manager.h" #include "gimple.h" #include "gimple-pass.h" #include "hashtab.h" #include "function.h" #include "basic-block.h" int plugin_is_GPL_compatible = 1; static bool g_mark_next_node = false; static htab_t g_marked_nodes; // 存储被标记的函数/if语句节点 // 哈希表辅助函数 static hashval_t node_hash(const void *p) { return (hashval_t)((uintptr_t)p); } static int node_eq(const void *p1, const void *p2) { return p1 == p2; } // 处理自定义#pragma my_pragma static void handle_my_pragma(cpp_reader *reader ATTRIBUTE_UNUSED) { g_mark_next_node = true; } // 注册自定义pragma static void register_pragma_callback(void *event_data ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED) { c_register_pragma("my_pragma", handle_my_pragma); } // 标记#pragma后的函数 static void mark_function(void *event_data, void *data ATTRIBUTE_UNUSED) { tree decl = (tree)event_data; if (g_mark_next_node && DECL_P(decl) && DECL_FUNCTION_P(decl)) { htab_insert(g_marked_nodes, decl); g_mark_next_node = false; } } // 标记#pragma后的if语句(生成GIMPLE前处理树结构) static void mark_if_stmt(void *event_data, void *data ATTRIBUTE_UNUSED) { function *fun = (function *)event_data; if (!g_mark_next_node || !fun) return; tree body = DECL_SAVED_TREE(fun->decl); if (!body) return; // 遍历函数体,找到第一个if语句并标记 tree stmt = (TREE_CODE(body) == COMPOUND_STMT) ? BLOCK_BEGIN(body) : body; for (; stmt; stmt = TREE_CHAIN(stmt)) { if (TREE_CODE(stmt) == IF_STMT) { htab_insert(g_marked_nodes, stmt); g_mark_next_node = false; break; } } }
2. 注册GIMPLE阶段自定义PASS
关键事件:PLUGIN_PASS_MANAGER_SETUP
通过该事件将自定义PASS插入到GCC编译流程中,示例选择在SSA分析之后执行,可根据需求调整位置。
代码实现
// 自定义PASS数据结构 const pass_data my_gimple_pass_data = { GIMPLE_PASS, /* PASS类型 */ "my_gimple_pass", /* PASS名称 */ OPTGROUP_NONE, /* 优化组 */ TV_NONE, /* 性能监控ID */ PROP_gimple_any, /* 依赖的GIMPLE属性 */ 0, /* 提供的属性 */ 0, /* 销毁的属性 */ 0, /* 起始需要处理的todo标记 */ 0 /* 结束时设置的todo标记 */ }; class my_gimple_pass : public gimple_opt_pass { public: my_gimple_pass(gcc::context *ctxt) : gimple_opt_pass(my_gimple_pass_data, ctxt) {} // 判断是否需要执行当前PASS virtual bool gate(function *fun) { return htab_find(g_marked_nodes, fun->decl) != NULL || !htab_empty(g_marked_nodes); } // PASS执行逻辑:处理标记的函数/if语句对应的GIMPLE virtual unsigned int execute(function *fun) { // 处理被标记的函数 if (htab_find(g_marked_nodes, fun->decl)) { fprintf(stderr, "[MY_PLUGIN] Processing marked function: %s\n", IDENTIFIER_POINTER(DECL_NAME(fun->decl))); } // 遍历基本块,匹配标记的if语句对应的GIMPLE条件 basic_block bb; gimple_stmt_iterator gsi; FOR_EACH_BB_FN(bb, fun) { for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { gimple *stmt = gsi_stmt(gsi); if (is_gimple_cond(stmt)) { location_t stmt_loc = gimple_location(stmt); // 遍历哈希表匹配源码位置 htab_traverse(g_marked_nodes, [&](htab_entry *e) -> int { tree if_tree = (tree)e->key; if (tree_location(if_tree) == stmt_loc) { fprintf(stderr, "[MY_PLUGIN] Found marked if stmt: %s\n", gimple_as_string(stmt, 0)); return 1; // 停止遍历 } return 0; }); } } } return 0; } }; // 注册自定义GIMPLE PASS static void register_my_pass(void *event_data, void *data ATTRIBUTE_UNUSED) { gcc::context *ctxt = static_cast<gcc::context *>(event_data); struct register_pass_info pass_info; pass_info.pass = new my_gimple_pass(ctxt); pass_info.reference_pass_name = "ssa"; // 在SSA PASS之后执行 pass_info.ref_pass_instance_number = 1; pass_info.pos_op = PASS_POS_INSERT_AFTER; register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); }
3. 插件入口函数
初始化哈希表并注册所有回调:
int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) { if (!plugin_default_version_check(version, &gcc_version)) { fprintf(stderr, "[MY_PLUGIN] Plugin version mismatch with GCC\n"); return 1; } // 初始化存储标记节点的哈希表 g_marked_nodes = htab_create(10, node_hash, node_eq, NULL); // 注册各类回调 register_callback(plugin_info->base_name, PLUGIN_PRAGMA, register_pragma_callback, NULL); register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, mark_function, NULL); register_callback(plugin_info->base_name, PLUGIN_PRE_GENERICIZE, mark_if_stmt, NULL); register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, register_my_pass, NULL); return 0; }
4. 编译与使用
编译插件
gcc -fPIC -shared -o my_plugin.so my_plugin.c \ -I$(gcc -print-file-name=plugin)/include \ -I$(gcc -print-file-name=plugin)/include/c-family \ -O2
使用插件编译目标代码
gcc -fplugin=./my_plugin.so test.c
注意事项
- GCC 10.3.0的头文件路径必须正确,可通过
gcc -print-file-name=plugin获取插件目录 - 当前if语句标记逻辑仅处理#pragma后的第一个if语句,可根据需求修改遍历逻辑
- GIMPLE阶段通过源码位置匹配节点,若需更精确匹配,可在标记阶段存储更多关联信息
内容的提问来源于stack exchange,提问作者BD26
相关产品推荐
相关产品推荐

