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;