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 示例。
扩展的源代码位于 ./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;