跳转到内容

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

来自维基教科书,开放世界中的开放书籍

在本节中,我们将逐步描述如何创建一个新的编译器扩展。首先,需要下载 GCC 并用 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;
上一个: GEM 框架 C 中的函数重载 下一个: GCC 黑客
华夏公益教科书