跳轉到內容

GNU C 編譯器內部/建立編譯器擴充套件 3 4

來自 Wikibooks,為開放世界提供開放書籍

在本節中,我們逐步描述了建立新的編譯器擴充套件的過程。首先,需要下載 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 示例。

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;
華夏公益教科書