GNU C 编译器内部/创建编译器扩展 3 4
在本节中,我们逐步描述了创建新的编译器扩展的过程。首先,需要下载 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 示例。
扩展的源代码位于 ./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;