跳转到内容

GNU C 编译器内部/创建编译器扩展 3 4

来自 Wikibooks,为开放世界提供开放书籍

在本节中,我们逐步描述了创建新的编译器扩展的过程。首先,需要下载 GCC 并使用 GEM 补丁对其进行修补。之后,就可以开始编写编译器扩展。扩展准备就绪后,就可以使用经过 GEM 修补的 GCC 版本和新的编译器扩展来编译其他程序。

这里 下载 GEM 框架。解压它并输入 make all。这将下载 GCC 的副本,使用 GCC 补丁对其进行修补,编译和安装 GCC,以及构建示例。

C 函数重载示例允许在 C 中像在 C++ 中一样重载函数

  /* one function */
  void test(int a, int b) {
    printf("%d %d\n", a, b);
  }
  /* overloaded function */
  void test(char *a, int b) {
    printf("%s %d\n", a, b);
  }
  void main() {
    test(1,2);
    test("abc", 1);
  }

让我们更详细地考虑 CFO 示例。

C 函数重载

[编辑 | 编辑源代码]

扩展的源代码位于 ./examples/cfo/cfo.c 文件中。实现了以下挂钩

 gem_start_decl()
 gem_start_function()
 gem_build_function_call()

实现的想法是修改多次声明的每个函数的名称。这些函数的名称被修改为包含参数的类型信息。例如,函数 test(int, int) 被重命名为 test_i_i(int, int)。重命名在函数 cfo_alias_decl() 中进行。

函数 cfo_start_decl() 和 gem_start_function() 调用此函数。它查找要声明的函数的名称,如果找不到该名称,则立即返回

 cfo_find_symtab(&t_func, func_name);
 if (t_func==NULL_TREE || DECL_BUILT_IN(t_func)) {
   return;
 }

因此,不会更改未重载函数的名称。这保持了与未修改编译器的兼容性。

如果之前已经声明了具有此名称的函数,则扩展会检查是否创建了包含参数类型信息的别名

 strcpy(new_name, func_name);
 strcat(new_name, cfo_build_name(TREE_PURPOSE(T_O(declarator, 1))));
 cfo_find_symtab(&t_func_alias, name);

函数 cfo_build_name() 构建函数参数的名称。如果之前未创建别名,则会创建一个别名。

   t_alias_attr=tree_cons(get_identifier("alias"),
                          tree_cons(NULL_TREE, get_identifier(name), NULL_TREE), NULL_TREE);
   TYPE_ATTRIBUTES(T_T(t_func)) = t_alias_attr;
   DECL_ATTRIBUTES(t_func)=t_alias_attr;

最后,新的声明被重命名,以便它包含参数的类型信息

   T_O(declarator,0) = get_identifier(new_name);

函数 cfo_build_function_call() 构建包含参数类型信息的函数的名称,并在符号表中查找它。如果找到了该名称,则在函数调用中替换函数名称。如果找不到该名称,则该函数未被重载,并且不会更改要调用的函数的名称。构建新名称

 name = cfo_build_decl_name(t_func, t_parm);

检查此标识符是否之前被解析过

 t_new_func = get_identifier(name);

如果被解析过,则获取声明

 if (t_new_func) {
   t_new_func = lookup_name(t_new_func);
 }

如果找到了声明,则替换函数

 *func = t_new_func;
华夏公益教科书