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;