跳轉到內容

GNU C 編譯器內部/函式編譯 4 1

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

函式序言和尾聲

[編輯 | 編輯原始碼]
GCC stack frame layout.
GCC 棧幀佈局。

序言用於初始化函式,例如設定其棧幀。函式尾聲用於完成執行。在每個編譯級別,每個函式都有一些常見的操作。因此,GCC 中存在許多序言/尾聲生成函式。

在 GIMPLE 級別,序言在函式 gimplify_function_tree() 中生成。如果使用 -finstrument-functions 指令,它將新增效能分析鉤子。

在 RTL 級別,使用函式 expand_function_start。它開始一個新函式的 RTL,並設定用於發出 RTL 的變數。它還為返回語句生成跳轉標籤,並決定返回值是在記憶體中還是在暫存器中。之後,它呼叫 assign_parms(),該函式將形式引數對映到實際引數。

機器級別的序言和尾聲在 pass flow2 的函式 thread_prologue_and_epilogue_insns() 中新增,該函式依賴於機器描述檔案,例如 i386.md。描述包含許多條目。在這種情況下,序言和尾聲條目被使用。它們分別設定為 ix86_expand_prologue() 和 ix86_expand_epilogue(),它們為 i386 架構生成 RTL 序言和尾聲。特定於機器的程式碼負責函式中使用的暫存器,以便在函式呼叫之間保留它們。push 指令作為函式的第一條指令新增。

特定於機器的程式碼作為 RTL 表示式而不是彙編程式碼生成,因為這是編譯器此階段使用的表示形式。RTL 表示形式是針對各種目標最通用的。因此,i386 的 push 指令對應於多個 RTL 表示式。

static rtx
gen_push (rtx arg)
{
  return gen_rtx_SET (VOIDmode,
                    gen_rtx_MEM (Pmode,
                                 gen_rtx_PRE_DEC (Pmode,
                                                  stack_pointer_rtx)),
                    arg);
}

在彙編輸出級別,序言在函式 assemble_start_function() 和 final_start_function() 中生成。它們的輸出主要與除錯相關。

需要注意的是,序言函式從較高級別表示形式到較低級別表示形式執行。在執行時,新增的程式碼的執行順序相反。也就是說,機器級別的序言首先執行。對於 i386,它儲存函式中使用的暫存器並設定一個新的棧幀。然後接收引數並呼叫效能分析鉤子。

區域性控制流分析

[編輯 | 編輯原始碼]

一個控制流圖基本塊組成,基本塊是一系列指令,只有一個入口和一個出口。型別 struct basic_block 描述它。有一個指向定義基本塊的語句列表的指標。每個基本塊還有一個指向前一個和下一個 BB 的指標。因此,可以將它們連結到列表中。欄位 preds 和 succs 可以訪問進入和離開塊的控制/資料流邊。

函式 create_basic_block() 接收第一個和最後一個語句,並在給定語句之後插入新的 BB。make_edge() 將兩個 BB 連結起來。

使用宏 FOR_EACH_BB 遍歷 CFG。每個函式流圖都有一個入口塊和一個出口塊,分別使用 ENTRY_BLOCK_PTR 和 EXIT_BLOCK_PTR 訪問。

函式 build_tree_cfg() 接收一個 GIMPLE 樹並生成 CFG。首先,它用兩個 BB 初始化 CFG:ENTRY_BLOCK 和 EXIT_BLOCK。它呼叫 make_blocks(),然後呼叫 make_edges()。

華夏公益教科書