GCC Plugin

Table of Contents

1. GCC Plugin

1.1. overview

gcc plugin 在 gcc 中定义了许多 hook (event), 用户可以在 so 中实现对应的 callback 来影响 gcc 的行为, 例如修改 AST 或添加 tree pass 或 rtl pass 来修改 GIMPLE 或 rtl. 通过 plugin 可以添加 instrumentation 或进行静态代码分析.

gcc 支持的 event 在 plugin.def 中, 主要包括以下几个 event:

  • PLUGIN_START_PARSE_FUNCTION
  • PLUGIN_FINISH_PARSE_FUNCTION

    解析完函数的 GENERIC 后, 参数为 func_decl

  • PLUGIN_PASS_MANAGER_SETUP

    添加一个 pass

  • PLUGIN_FINISH_TYPE

    解析完一个 type 后, 例如 struct x {}. 调用这个 hook 的参数是 type 对应的 tree

  • PLUGIN_FINISH_DECL

    解析完一个 decl 后, 例如 int x. 参数为对应的 decl, 例如 var_decl 或 param_decl

  • PLUGIN_FINISH_UNIT
  • PLUGIN_PRE_GENERICIZE

    解析完函数的 AST 后, 参数为 func_decl.

  • PLUGIN_ATTRIBUTES
  • PLUGIN_START_UNIT
  • PLUGIN_PRAGMAS
  • PLUGIN_ALL_PASSES_START

    第一个 pass 之前, 没有参数

  • PLUGIN_ALL_PASSES_END

    最后一个 pass 之后, 没有参数

  • PLUGIN_OVERRIDE_GATE

    每个 pass 执行前, 参数为 gate 原始的结果, 通过它可以控制 pass 是否执行

  • PLUGIN_PASS_EXECUTION
  • PLUGIN_INCLUDE_FILE

1.2. tree

https://gcc.gnu.org/onlinedocs/gccint/GENERIC.html#GENERIC

https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html#GIMPLE

https://www.cse.iitb.ac.in/grc/gcc-workshop-13/downloads/slides/Day1/gccw13-gimple-manipulation.pdf

gcc plugin 操作的数据主要是 HIR (除了使用 plugin 自定义 rtl pass), 例如:

  • PLUGIN_PRE_GENERICIZE 操作的是 AST
  • PLUGIN_FINISH_PARSE_FUNCTION 操作的是 GENERIC
  • PLUGIN_PASS_MANAGER_SETUP 添加的 gimple_opt_pass 操作的是 GIMPLE

这三种 HIR 都会使用 tree 来表示, 只不过它们使用的 tree node 类型会有些差别:

  1. AST

    gcc frontend 的输出是 AST (abstract syntax tree), 对应具体的语言. 不同语言的 AST 会有些差别. 例如 c 语言中的 `for` 会对应 `FOR_STMT`, 定义在 c-common.def 中

  2. GENERIC

    gcc 会把语言相关的 AST 转换成通用的 GENERIC. c 语言中的 `FOR_STMT` 会被转换成通用的 `LOOP_EXPR`, 定义在 tree.def 中.

  3. GIMPLE

    为了便于优化, gcc 会把 GENERIC 转换成 GIMPLE, GIMPLE 采用三地址形式, 且控制流变成 GOTO 的形式, 例如前面提到的 `LOOP_EXPR` 会转换成 `GIMPLE_GOTO`, 定义在 gimple.def 中

例如一个 function 的声明部分对应的 tree 通过 debug_tree 函数打印出来的 GENERIC 为:

<function_decl 0x7fe180089400 main

   type <function_type 0x7fe180085888
       type <integer_type 0x7fe17ff745e8 int public SI
           size <integer_cst 0x7fe17ff5bee8 constant 32>
           unit-size <integer_cst 0x7fe17ff5bf00 constant 4>
           align:32 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fe17ff745e8 precision:32 min <integer_cst 0x7fe17ff5bea0 -2147483648> max <integer_cst 0x7fe17ff5beb8 2147483647>
           pointer_to_this <pointer_type 0x7fe17ff7c9d8>>
       QI
       size <integer_cst 0x7fe17ff5bd98 constant 8>
       unit-size <integer_cst 0x7fe17ff5bdb0 constant 1>
       align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fe180085888
       arg-types <tree_list 0x7fe180092500 value <integer_type 0x7fe17ff745e8 int>
           chain <tree_list 0x7fe1800924d8 value <pointer_type 0x7fe1800857e0>
               chain <tree_list 0x7fe17ff707d0 value <void_type 0x7fe17ff74f18 void>>>>>

   public static QI test.c:2:5 align:8 warn_if_not_align:0 initial <block 0x7fe18008e420>

   result <result_decl 0x7fe17ff68bb8 D.1913 type <integer_type 0x7fe17ff745e8 int>
       ignored SI test.c:2:5 size <integer_cst 0x7fe17ff5bee8 32> unit-size <integer_cst 0x7fe17ff5bf00 4>
       align:32 warn_if_not_align:0 context <function_decl 0x7fe180089400 main>>

   arguments
       <parm_decl 0x7fe1800b0000 argc type <integer_type 0x7fe17ff745e8 int>
       SI test.c:2:14 size <integer_cst 0x7fe17ff5bee8 32> unit-size <integer_cst 0x7fe17ff5bf00 4>
       align:32 warn_if_not_align:0 context <function_decl 0x7fe180089400 main> arg-type <integer_type 0x7fe17ff745e8 int>

       chain

       <parm_decl 0x7fe1800b0080 argv type <pointer_type 0x7fe1800857e0>
           unsigned decl_0 DI test.c:2:26
           size <integer_cst 0x7fe17ff5bca8 constant 64>
           unit-size <integer_cst 0x7fe17ff5bcc0 constant 8>
           align:64 warn_if_not_align:0 context <function_decl 0x7fe180089400 main> arg-type <pointer_type 0x7fe1800857e0>>>

   struct-function 0x7fe1800b1000>

它包含了函数声明的所有信息: 函数的名字, 参数和返回值的名字和类型

这里面所有的 <> 都表示不同 tree 类型, 例如:

  • function_decl
  • function_type
  • integer_type
  • integer_cst
  • result_decl
  • parm_decl

这个 function_decl 相当于如下的伪代码:

function_decl {
    .name = "main",
    .type = function_type {
        .type = integer_type {
            .size = integer_cst {
                .val = 32,
            }
            ...
        }
        ...
    },
    .result = result_decl {
        .type = integer_type {
        }
    }
    .arguments = chain(
        param_decl {
            .name = "argc",
            ...
        },
        param_decl {
            .name = "argv",
            ...
        }
    );
}

gcc 的 tree 类型相当于一个接口, 各个具体的 tree 类型相当于抽象类或具体的派生类, 但这种类的层次关系是通过 c 的 union 和 struct 来实现的.

union tree_node {
    struct tree_base base;
    struct tree_typed typed;
    struct tree_common common;
    struct tree_int_cst int_cst;
    struct tree_poly_int_cst poly_int_cst;
    /* ... */
};

struct tree_typed {
    struct tree_base base;
    tree type;
};

struct tree_common {
    struct tree_typed typed;
    tree chain;
};

struct tree_int_cst {
    struct tree_typed typed;
    HOST_WIDE_INT val[1];
};

即 tree_int_cst 的派生树为: base <- typed <- int_cst:

  • 通过 x.int_cst.val 访问一个 int_cst tree 的 val 成员,
  • 通过 x.int_cst.typed.base.code 或者 x.base.code 访问 int_cst 的 tree code

因为 union 的原因, x.int_cst.typed.basex.base 实际是相同的地址. gcc 用 union 和 struct 模拟了 c++ 的对象模型, 具体的可以参考 Inside The C++ Object Model

gcc 提供了大量的宏和函数来方便访问不同类型的 tree 中的数据, 例如:

  • TREE_CODE, 适用于所有的 tree 类型, 返回 x.base.code, 即 tree code
  • TREE_TYPE, 适用于继承自 tree_typed 的类型, 返回 x.typed.type, 例如 function decl 对应的 type, 或者 expr 的 type
  • TREE_CHAIN 用来获得 chain 中的下一个 node, 例如前面的 arguments
  • TREE_OPERAND, 适用于继承自 tree_exp 类型, 获得 exp 的 operand
  • DECL_XXX, 适用于 decl 类的 tree, 例如 function_decl, var_decl 等, 获得 decl 相关信息, 例如 DECL_NAME, DECL_RESULT, DECL_ARGUMENTS
  • TYPE_XXX, 获得 type 具体的信息, 例如 TYPE_NAME, TYPE_SIZE
  • IDENTIFIER_POINTER, 适用于继承自 tree_identifier 的类型, 得到其 identifier 字符串, 例如 IDENTIFIER_POINTER(DECL_NAME(function_decl)) 可以得到函数声明的名字
  • DECL_SAVED_TREE 用来得到 function_decl 的函数体
  • tree_stmt_iterator 访问 statement_list 中的 stmt
  • debug_tree 可以 dump 整个 tree
  • walk_tree 可以 walk 整个 tree, 以遍历 tree 的 list, vec, operand 等
  • build_xxx 可以构造新的 tree, 例如 build_int_cst, build_fn_decl, build_string_literal, …
  • BIND_EXPR_BODY, BIND_EXPR_VARS 用来操作 block 中的 stmt 和 var

以前面的 function_decl 为例:

assert(TREE_CODE(TREE_TYPE(DECL_RESULT(x))) == INTEGER_TYPE);
assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(DECL_ARGUMENTS(x))), "argc") == 0);
assert(TREE_INT_CST_ELT(TYPE_SIZE(TREE_TYPE(x)), 0) == 32);

http://icps.u-strasbg.fr/~pop/gcc-ast.html 用图片展示了几个简单的 AST 结构, 有些可能会有变化, 可以先通过 debug_tree 打印出 tree 的结构, 然后看看看用哪个宏或函数可以操作关心的数据, 具体需要去查找 tree.h 以及对调试非常有用的 print-tree.htree-pretty-print.h

1.2.1. GIMPLE

操作 AST 和 GENERIC 都是用宏操作 union 的方式, 但 GIMPLE 不再用 tree (或者 union), 而不是用 c++ class 来描述类型的层次结构, 例如 gimple, gcall, gassign, 相应的提供了和 tree 的宏类似的函数访问其成员, 例如 gimple_code (对应 TREE_CODE), gimple_get_lhs (对应 TREE_OPERAND), …

但 GIMPLE 并没有完全代替 tree, 它主要是针对 stmt 提供了另一个封装, 其它部分(例如 name, type, decl 等) 还会用 tree 来表示, 例如 gimple_get_lhs 返回的仍然是一个 tree (一个 var_decl).

关于 GIMPLE 的用法需要看一下 gimple.hgimple-pretty-print.h

Backlinks

GCC (GCC > GCC Plugin): GCC Plugin

Author: [email protected]
Date: 2023-03-30 Thu 16:03
Last updated: 2023-06-29 Thu 18:30

知识共享许可协议