You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在GCC 10.3.0插件中实现自定义#pragma标记代码的技术问询

GCC 10.3.0插件实现自定义#pragma及GIMPLE阶段处理方案

核心思路

通过注册多个GCC插件事件回调,分三步实现需求:

  1. 解析自定义#pragma my_pragma,设置标记开关
  2. 在树阶段捕获函数/if语句,将符合条件的节点存入全局哈希表,完成标记传递
  3. 注册自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 07:25:09