1 TableGen 後端開發者指南

1.1 簡介

TableGen 的目的是根據原始檔中的資訊產生複雜的輸出檔案,這些原始檔比輸出檔案更容易編寫程式碼,並且更容易隨著時間的推移進行維護和修改。這些資訊以涉及類別和記錄的宣告式樣式編碼,然後由 TableGen 進行處理。內部化的記錄會傳遞到各種後端,這些後端從記錄的子集中提取資訊並產生輸出檔案。這些輸出檔案通常是 C++ 的 .inc 檔案,但也可能是後端開發人員需要的任何類型的檔案。

本文檔是編寫 TableGen 後端的指南。它不是完整的參考手冊,而是使用 TableGen 為後端提供的功能的指南。如需有關所涉及的各種資料結構和函數的完整參考,請參閱主要的 TableGen 標頭檔 (record.h) 和/或 Doxygen 文件。

本文檔假設您已閱讀 TableGen 程式設計師參考,其中提供了編寫 TableGen 原始檔的詳細參考。如需現有後端的說明,請參閱 TableGen 後端

1.2 資料結構

以下各節描述包含由 TableGen 解析器從 TableGen 原始檔收集的類別和記錄的資料結構。請注意,術語「類別」指的是抽象記錄類別,而術語「記錄」指的是具體記錄。

除非另有說明,否則與類別關聯的函數都是實例函數。

1.2.1 RecordKeeper

RecordKeeper 類別的實例充當由 TableGen 解析和收集的所有類別和記錄的容器。當 TableGen 調用後端時,RecordKeeper 實例會傳遞給後端。此類別通常縮寫為 RK

記錄管理員中有兩個映射,一個用於類別,一個用於記錄(後者通常稱為「定義」)。每個映射將類別或記錄名稱映射到 Record 類別的實例(請參閱 Record),其中包含有關該類別或記錄的所有資訊。

除了兩個映射之外,RecordKeeper 實例還包含:

  • 將全域變數的名稱映射到其值的映射。全域變數是在 TableGen 檔案中使用外部 defvar 語句定義的。

  • 用於命名匿名記錄的計數器。

RecordKeeper 類別提供了一些有用的函數。

  • 用於獲取完整的類別和記錄映射的函數。

  • 用於根據父類別獲取記錄子集的函數。

  • 用於按名稱獲取個別類別、記錄和全域變數的函數。

可以使用 << 運算符將 RecordKeeper 實例列印到輸出串流。

1.2.2 Record

TableGen 建立的每個類別或記錄都由 Record 類別的實例表示。RecordKeeper 實例包含一個用於類別的映射和一個用於記錄的映射。記錄的主要資料成員是記錄名稱、欄位名稱及其值的向量,以及記錄的超類別向量。

記錄名稱儲存為指向 Init 的指標(請參閱 Init),Init 是一個類別,其實例持有 TableGen 值(有時稱為*初始設定式*)。欄位名稱和值儲存在 RecordVal 實例的向量中(請參閱 RecordVal),每個實例都包含欄位名稱及其值。超類別向量包含一系列配對,每個配對都包含超類別記錄及其來源檔案位置。

除了這些成員之外,Record 實例還包含:

  • 一個來源檔案位置向量,其中包含記錄定義本身,以及其定義中涉及的任何多類別的位置。

  • 對於類別記錄,則為類別範本引數的向量。

  • 對應於此記錄的 DefInit 實例(請參閱 DefInit)。

  • 唯一的記錄 ID。

  • 一個布林值,指定這是否為類別定義。

  • 一個布林值,指定這是否為匿名記錄。

Record 類別提供了許多有用的函式。

  • 用於取得記錄名稱、欄位、來源檔案位置、範本引數和唯一 ID 的函式。

  • 用於取得記錄的所有超類別或僅取得其直接超類別的函式。

  • 用於透過指定各種形式的欄位名稱來取得特定欄位值,並以各種形式傳回其值的函式(請參閱 取得記錄名稱和欄位)。

  • 用於檢查記錄各種屬性的布林函式。

可以使用 << 運算子將 Record 實例列印到輸出串流。

1.2.3 RecordVal

記錄的每個欄位都儲存在 RecordVal 類別的實例中。Record 實例包含這些值實例的向量。RecordVal 實例包含欄位的名稱,儲存在 Init 實例中。它還包含欄位的值,同樣儲存在 Init 中。(這個類別更好的名稱可能是 RecordField。)

除了這些主要成員之外,RecordVal 還有其他資料成員。

  • 欄位定義的來源檔案位置。

  • 欄位的類型,儲存為 RecTy 類別的實例(請參閱 RecTy)。

RecordVal 類別提供了一些有用的函式。

  • 以各種形式取得欄位名稱的函數。

  • 取得欄位類型的函數。

  • 取得欄位值的函數。

  • 取得來源檔案位置的函數。

請注意,欄位值更容易直接從 Record 實例中取得(請參閱Record)。

RecordVal 實例可以使用 << 運算子輸出到輸出串流。

1.2.4 RecTy

RecTy 類別用於表示欄位值的類型。它是為每個可用欄位類型定義的子類別系列的基類。RecTy 類別有一個資料成員,它是一個指定特定欄位值類型的列舉類型。(這個類別更好的名稱可能是 FieldTy。)

RecTy 類別提供了一些有用的函數。

  • 一個虛擬函數,用於取得作為字串的類型名稱。

  • 一個虛擬函數,用於檢查此類型的所有值是否可以轉換為另一個給定的類型。

  • 一個虛擬函數,用於檢查此類型是否是另一個給定類型的子類型。

  • 一個函數,用於取得具有此類型元素的列表的對應 list 類型。例如,當使用 int 類型呼叫時,該函數會返回 list<int> 類型。

繼承自 RecTy 的子類別有 BitRecTyBitsRecTyCodeRecTyDagRecTyIntRecTyListRecTyRecordRecTyStringRecTy。其中一些類別還有一些額外的成員,這些成員將在以下小節中進行說明。

所有 繼承自 RecTy 的類別都提供 get() 函數。它返回一個對應於派生類別的 Recty 實例。一些 get() 函數需要一個參數來指定所需的特定類型變體。這些參數將在以下小節中進行說明。

RecTy 實例可以使用 << 運算子輸出到輸出串流。

警告

未指定特定類型的 RecTy 實例是單個還是多個。

1.2.4.1 BitsRecTy

此類別包含一個具有 bits 值大小的資料成員,以及一個用於取得該大小的函數。

get() 函式會取得序列的長度 n,並返回對應於 bits<n>BitsRecTy 類型。

1.2.4.2 ListRecTy

此類別包含一個資料成員,用於指定清單元素的類型,以及一個用於取得該類型的函式。

get() 函式會取得清單成員的 RecTy 類型,並返回對應於 list<type>ListRecTy 類型。

1.2.4.3 RecordRecTy

此類別包含資料成員,其中包含此紀錄之父類別的清單。它也提供了一個用於取得類別陣列的函式,以及兩個用於取得迭代器 begin()end() 值的函式。此類別定義了後兩個函式之返回值的類型。

using const_record_iterator = Record * const *;

get() 函式會取得一個指向紀錄之直接 父類別之 Record 執行個體的 ArrayRef 指標,並返回繼承自這些父類別的紀錄所對應的 RecordRecTy

1.2.5 Init

Init 類別用於表示 TableGen 值。名稱源自於初始化值。此類別不應與 RecordVal 類別混淆,後者表示紀錄欄位,包括其名稱和值。Init 類別是一系列子類別的基底類別,每個子類別都對應一種可用的值類型。Init 的主要資料成員是一種列舉類型,表示值的特定類型。

Init 類別提供了一些有用的函式。

  • 一個用於取得類型列舉值的函式。

  • 一個布林虛擬函式,用於判斷值是否已完全指定;也就是說,沒有未初始化的子值。

  • 虛擬函式,用於取得值作為字串。

  • 虛擬函式,用於將值轉換為其他類型,實現 TableGen 的位元範圍功能,以及實現清單切片功能。

  • 一個虛擬函式,用於取得值的特定位元。

直接繼承自 Init 的子類別是 UnsetInitTypedInit

可以使用 << 運算子將 Init 執行個體列印到輸出串流。

警告

並未具體說明兩個具有相同底層類型和值的獨立初始值(例如,兩個值為「Hello」的字符串)是由兩個 Init 表示,還是共用同一個 Init

1.2.5.1 UnsetInit

這個類別是 Init 的子類別,表示未設定(未初始化)的值。可以使用靜態函數 get() 來獲取此類型的單例 Init

1.2.5.2 TypedInit

這個類別是 Init 的子類別,作為表示特定值類型(除了未設定值)的類別的父類別。這些類別包括 BitInitBitsInitDagInitDefInitIntInitListInitStringInit。(TableGen 解析器使用了其他衍生類型。)

這個類別包含一個數據成員,用於指定值的 RecTy 類型。它提供了一個函數來獲取該 RecTy 類型。

1.2.5.3 BitInit

BitInit 類別是 TypedInit 的子類別。其實例表示一個位元的可能值:0 或 1。它包含一個數據成員,其中包含該位元。

所有 衍生自 TypedInit 的類別都提供以下函數。

  • 一個名為 get() 的靜態函數,它返回一個表示指定值(一個或多個)的 Init。在 BitInit 的情況下,get(true) 返回一個表示 true 的 BitInit 的實例,而 get(false) 返回一個表示 false 的實例。如上所述,並未具體說明表示 true(或 false)的 BitInit 究竟只有一個還是多個。

  • 一個名為 GetValue() 的函數,它以更直接的形式返回實例的值,在這種情況下為 bool

1.2.5.4 BitsInit

BitsInit 類別是 TypedInit 的子類別。其實例表示位元序列,從高位到低位。它包含一個數據成員,其中包含序列的長度和一個指向 Init 實例的向量,每個位元一個。

這個類別提供了常見的 get() 函式。它不提供 getValue() 函式。

此類別提供下列額外函式。

  • 一個用於取得序列中位元數的函式。

  • 一個透過整數索引取得特定位元的函式。

1.2.5.5 DagInit

DagInit 類別是 TypedInit 的子類別。它的實例表示可能的「有向無環圖」(dag)。

此類別包含一個指向 DAG 運算子的 Init 的指標,以及一個指向運算子名稱的 StringInit 的指標。它包含 DAG 運算元的數量和運算元名稱的數量。最後,它包含一個指向運算元之 Init 實例的指標向量,以及另一個指向運算元名稱之 StringInit 實例的指標向量。(DAG 運算元也被稱為「參數」。)

此類別提供了常見 get() 函式的兩種形式。它不提供常見的 getValue() 函式。

此類別提供了許多額外函式。

  • 以各種形式取得運算子和以各種形式取得運算子名稱的函式。

  • 判斷是否有任何運算元以及取得運算元數量的函式。

  • 以個別和整體方式取得運算元的函式。

  • 判斷是否有任何名稱以及取得名稱數量的函式。

  • 以個別和整體方式取得名稱的函式。

  • 取得運算元迭代器 begin()end() 值的函式。

  • 取得名稱迭代器 begin()end() 值的函式。

此類別為運算元和名稱迭代器的回傳值定義了兩種類型。

using const_arg_iterator = SmallVectorImpl<Init*>::const_iterator;
using const_name_iterator = SmallVectorImpl<StringInit*>::const_iterator;

1.2.5.6 DefInit

DefInit 類別是 TypedInit 的子類別。它的實例表示由 TableGen 收集的記錄。它包含一個數據成員,該成員是指向記錄之 Record 實例的指標。

此類別提供了常見的 get() 函式。它不提供 getValue()。相反,它提供了 getDef(),它會回傳 Record 實例。

1.2.5.7 IntInit

IntInit 類別是 TypedInit 的子類別。其實例表示 64 位元整數的可能值。它包含一個含有整數值的資料成員。

此類別提供常用的 get()getValue() 函式。後者以 int64_t 類型返回整數值。

此類別還提供了一個函式 getBit(),用於取得整數值的指定位元。

1.2.5.8 ListInit

ListInit 類別是 TypedInit 的子類別。其實例表示某種類型元素的清單。它包含一個帶有清單長度的資料成員,以及一個指向 Init 實例的指標向量,每個元素一個。

此類別提供常用的 get()getValues() 函式。後者返回一個指向 Init 實例的指標向量的 ArrayRef

此類別還提供以下額外函式。

  • 一個用於取得元素類型的函式。

  • 用於取得向量長度和判斷向量是否為空的函式。

  • 用於透過整數索引取得指定元素,並以各種形式返回的函式。

  • 用於取得迭代器 begin()end() 值的函式。此類別為這兩個函式的返回類型定義了一個類型。

using const_iterator = Init *const *;

1.2.5.9 StringInit

StringInit 類別是 TypedInit 的子類別。其實例表示任意長度的字串。它包含一個含有值的 StringRef 的資料成員。

此類別提供常用的 get()getValue() 函式。後者返回 StringRef

1.3 建立新的後端

為 TableGen 建立新的後端需要以下步驟。

  1. 為您的後端 C++ 檔案命名,例如 GenAddressModes

  2. 使用檔案 TableGenBackendSkeleton.cpp 作為起點,編寫新的後端。

  3. 確定哪個 TableGen 實例需要新的後端。Clang 和 LLVM 各有一個實例。或者您可能正在構建自己的實例。

  4. 將您的後端 C++ 檔案添加到相應的 CMakeLists.txt 檔案中,以便構建它。

  5. 將您的 C++ 檔案添加到系統中。

1.4 後端框架

檔案 TableGenBackendSkeleton.cpp 提供了一個骨架 C++ 轉譯單元,用於編寫新的 TableGen 後端。以下是一些關於該檔案的注意事項。

  • 包含清單是大多數後端所需的最小清單。

  • 與所有 LLVM C++ 檔案一樣,它有一個 using namespace llvm; 語句。它還有一個匿名命名空間,其中包含所有檔案特定的資料結構定義,以及包含發射器資料成員和函數的類別。繼續以 GenAddressModes 為例,這個類別被命名為 AddressModesEmitter

  • 發射器類別的建構函數接受一個 RecordKeeper 參考,通常命名為 RKRecordKeeper 參考被儲存在一個資料成員中,以便可以從中取得記錄。這個資料成員通常命名為 Records

  • 其中一個函數命名為 run。它由後端的「主要函數」呼叫,用於收集記錄並發射輸出檔案。它接受一個 raw_ostream 類別的實例,通常命名為 OS。輸出檔案是透過寫入這個串流來發射的。

  • run 函數應該使用 emitSourceFileHeader 輔助函數在發射的檔案中包含標準標頭。

  • 使用 llvm/TableGen/TableGenBackend.h 將類別或函數註冊為命令列選項。

    • 如果類別具有建構函數 (RK) 和方法 run(OS),請使用 llvm::TableGen::Emitter::OptClass<AddressModesEmitter>

    • 否則,請使用 llvm::TableGen::Emitter::Opt

本文檔其餘部分中的所有範例都將假設使用骨架檔案中使用的命名慣例。

1.5 取得類別

RecordKeeper 類別提供了兩個函數,用於取得 TableGen 檔案中定義的類別的 Record 實例。

  • getClasses() 返回所有類別的 RecordMap 參考。

  • getClass(名稱) 返回指定名稱類別的 Record 參考。

如果您需要迭代所有類別記錄

for (auto ClassPair : Records.getClasses()) {
  Record *ClassRec = ClassPair.second.get();
  ...
}

ClassPair.second 取得類別的 unique_ptr,然後 .get() 取得類別 Record 本身。

1.6 取得記錄

RecordKeeper 類別提供了四個函數,用於取得 TableGen 檔案中定義的具體記錄的 Record 實例。

  • getDefs() 會傳回一個 RecordMap 的參考,其中包含所有具體記錄。

  • getDef(name) 會傳回一個名為具體記錄的 Record 參考。

  • getAllDerivedDefinitions(classname) 會傳回一個 Record 參考向量,其中包含從指定類別衍生的具體記錄。

  • getAllDerivedDefinitions(classnames) 會傳回一個 Record 參考向量,其中包含從所有指定類別衍生的具體記錄。

這段敘述會取得從 Attribute 類別衍生的所有記錄,並逐一迭代。

auto AttrRecords = Records.getAllDerivedDefinitions("Attribute");
for (Record *AttrRec : AttrRecords) {
  ...
}

1.7 取得記錄名稱和欄位

如上所述(請參閱 Record),有多個函數可以傳回記錄的名稱。其中一個特別實用的函數是 getNameInitAsString(),它會以 std::string 的形式傳回名稱。

也有多個函數可以傳回記錄的欄位。若要取得並迭代所有欄位

for (const RecordVal &Field : SomeRec->getValues()) {
  ...
}

您應該還記得,RecordVal 類別的實例包含有關記錄中欄位的資訊。

getValue() 函數會傳回由名稱指定的欄位的 RecordVal 實例。此函數有多個多載版本,有些採用 StringRef,而有些則採用 const Init *。有些函數會傳回 RecordVal *,而有些則會傳回 const RecordVal *。如果欄位不存在,則會顯示致命的錯誤訊息。

在大多數情況下,您感興趣的是欄位的值,而不是 RecordVal 中的所有資訊。有一大堆函數可以採用某種形式的欄位名稱並傳回其值。其中一個函數 getValueInit 會以 Init * 的形式傳回值。另一個函數 isValueUnset 會傳回一個布林值,指定該值是否未設定(未初始化)。

大多數函數會以更有用的形式傳回值。例如

std::vector<int64_t> RegCosts =
    SomeRec->getValueAsListOfInts("RegCosts");

假設欄位 RegCosts 是一個整數清單。該清單會以 64 位元整數的 std::vector 形式傳回。如果該欄位不是整數清單,則會顯示致命的錯誤訊息。

以下是一個函數,它會將欄位值作為 Record 傳回,但如果欄位不存在,則會傳回 null。

if (Record *BaseRec = SomeRec->getValueAsOptionalDef(BaseFieldName)) {
  ...
}

假設此欄位的值為另一筆記錄。該記錄會以指向 Record 的指標形式返回。如果欄位不存在或未設定,則函數會返回 null。

1.8 取得記錄的父類別

Record 類別提供了一個函數來取得記錄的父類別。此函數名為 getSuperClasses,並返回一個 ArrayRef,其中包含一個 std::pair 對的陣列。父類別以後序排列:即在將其欄位複製到記錄時訪問父類別的順序。每個對都包含一個指向父類別記錄的 Record 實例的指標,以及一個 SMRange 類別的實例。該範圍指示類別定義開始和結束的原始檔案位置。

以下範例取得 Prototype 記錄的父類別,然後迭代返回陣列中的每個對。

ArrayRef<std::pair<Record *, SMRange>>
    Superclasses = Prototype->getSuperClasses();
for (const auto &SuperPair : Superclasses) {
  ...
}

Record 類別還提供了一個函數 getDirectSuperClasses,用於將記錄的*直接*父類別附加到給定的 SmallVectorImpl<Record *> 類型的向量中。

1.9 將文字發送到輸出串流

run 函數會傳入一個 raw_ostream,用於列印輸出檔案。按照慣例,此串流會儲存在名為 OS 的發送器類別成員中,不過有些 run 函數很簡單,只會使用串流而不儲存它。輸出可以透過將值直接寫入輸出串流,或使用 std::format()llvm::formatv() 函數來產生。

OS << "#ifndef " << NodeName << "\n";

OS << format("0x%0*x, ", Digits, Value);

以下類別的實例可以使用 << 運算子列印:RecordKeeperRecordRecTyRecordValInit

輔助函數 emitSourceFileHeader() 會列印應包含在每個輸出檔案頂部的標頭註解。在骨架後端檔案 TableGenBackendSkeleton.cpp 中包含對此函數的呼叫。

1.10 列印錯誤訊息

TableGen 記錄通常衍生自多個類別,並且也經常透過一系列的多類別來定義。因此,後端可能難以回報具有準確原始程式碼位置的清晰錯誤訊息。為了簡化錯誤回報,提供了五個錯誤回報函數,每個函數都有四個重載。

  • PrintWarning 會列印標記為警告的訊息。

  • PrintError 會列印標記為錯誤的訊息。

  • PrintFatalError 會列印標記為錯誤的訊息,然後終止程式。

  • PrintNote 會列印註記。它通常用於上述函數之後,以提供更多資訊。

  • PrintFatalNote 會列印註記,然後終止程式。

這五個函數都重載了四次。

  • PrintError(const Twine &Msg):列印訊息,但不包含原始程式碼位置。

  • PrintError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg):列印訊息,後跟指定的原始程式碼行,以及指向錯誤項目的指標。原始程式碼位置陣列通常取自 Record 實例。

  • PrintError(const Record *Rec, const Twine &Msg):列印訊息,後跟與指定記錄相關聯的原始程式碼行(請參閱記錄)。

  • PrintError(const RecordVal *RecVal, const Twine &Msg):列印訊息,後跟與指定記錄欄位相關聯的原始程式碼行(請參閱RecordVal)。

使用這些函數的目標是產生盡可能具體的錯誤報告。

1.11 除錯工具

TableGen 提供了一些工具來幫助除錯後端。

1.11.1 PrintRecords 後端

TableGen 命令選項 --print-records 會呼叫一個簡單的後端,用於列印原始程式碼中定義的所有類別和記錄。這是預設的後端選項。輸出的格式保證會隨著時間推移保持不變,以便可以在測試中比較輸出。輸出如下所示

------------- Classes -----------------
...
class XEntry<string XEntry:str = ?, int XEntry:val1 = ?> { // XBase
  string Str = XEntry:str;
  bits<8> Val1 = { !cast<bits<8>>(XEntry:val1){7}, ... };
  bit Val3 = 1;
}
...
------------- Defs -----------------
def ATable {  // GenericTable
  string FilterClass = "AEntry";
  string CppTypeName = "AEntry";
  list<string> Fields = ["Str", "Val1", "Val2"];
  list<string> PrimaryKey = ["Val1", "Val2"];
  string PrimaryKeyName = "lookupATableByValues";
  bit PrimaryKeyEarlyOut = 0;
}
...
def anonymous_0 {     // AEntry
  string Str = "Bob";
  bits<8> Val1 = { 0, 0, 0, 0, 0, 1, 0, 1 };
  bits<10> Val2 = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
}

類別會顯示其範本參數、父類別(跟在 // 之後)和欄位。記錄會顯示其父類別和欄位。請注意,匿名記錄的名稱為 anonymous_0anonymous_1 等。

1.11.2 PrintDetailedRecords 後端

TableGen 命令選項 --print-detailed-records 會呼叫一個後端,用於列印原始程式碼中定義的所有全域變數、類別和記錄。輸出的格式*不*保證會隨著時間推移保持不變。輸出如下所示。

DETAILED RECORDS for file llvm-project\llvm\lib\target\arc\arc.td

-------------------- Global Variables (5) --------------------

AMDGPUBufferIntrinsics = [int_amdgcn_s_buffer_load, ...
AMDGPUImageDimAtomicIntrinsics = [int_amdgcn_image_atomic_swap_1d, ...
...
-------------------- Classes (758) --------------------

AMDGPUBufferLoad  |IntrinsicsAMDGPU.td:879|
  Template args:
    LLVMType AMDGPUBufferLoad:data_ty = llvm_any_ty  |IntrinsicsAMDGPU.td:879|
  Superclasses: (SDPatternOperator) Intrinsic AMDGPURsrcIntrinsic
  Fields:
    list<SDNodeProperty> Properties = [SDNPMemOperand]  |Intrinsics.td:348|
    string LLVMName = ""  |Intrinsics.td:343|
...
-------------------- Records (12303) --------------------

AMDGPUSample_lz_o  |IntrinsicsAMDGPU.td:560|
  Defm sequence: |IntrinsicsAMDGPU.td:584| |IntrinsicsAMDGPU.td:566|
  Superclasses: AMDGPUSampleVariant
  Fields:
    string UpperCaseMod = "_LZ_O"  |IntrinsicsAMDGPU.td:542|
    string LowerCaseMod = "_lz_o"  |IntrinsicsAMDGPU.td:543|
...
  • 使用外部 defvar 語句定義的全域變數會顯示其值。

  • 類別會顯示其原始程式碼位置、範本參數、超類別和欄位。

  • 記錄會顯示其原始程式碼位置、defm 序列、超類別和欄位。

超類別會依照處理順序顯示,間接超類別則以括號表示。每個欄位都會顯示其值和設定值的來源位置。defm 序列會按照呼叫順序,顯示參與產生紀錄的 defm 語句的位置。

1.11.3 TableGen 計時階段

TableGen 提供一個階段計時功能,可以產生報告,顯示解析來源檔案和執行選定後端各個階段所花費的時間。可以使用 TableGen 命令的 --time-phases 選項啟用此功能。

如果後端*沒有*針對計時進行檢測,則會產生如下所示的報告。這是針對 AMDGPU 目標執行 --print-detailed-records 後端的計時結果。

===-------------------------------------------------------------------------===
                             TableGen Phase Timing
===-------------------------------------------------------------------------===
  Total Execution Time: 101.0106 seconds (102.4819 wall clock)

   ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---
  85.5197 ( 84.9%)   0.1560 ( 50.0%)  85.6757 ( 84.8%)  85.7009 ( 83.6%)  Backend overall
  15.1789 ( 15.1%)   0.0000 (  0.0%)  15.1789 ( 15.0%)  15.1829 ( 14.8%)  Parse, build records
   0.0000 (  0.0%)   0.1560 ( 50.0%)   0.1560 (  0.2%)   1.5981 (  1.6%)  Write output
  100.6986 (100.0%)   0.3120 (100.0%)  101.0106 (100.0%)  102.4819 (100.0%)  Total

請注意,後端的所有時間都歸類在「後端整體」之下。

如果後端已針對計時進行檢測,則其處理過程會分為多個階段,並分別計時。這是針對 AMDGPU 目標執行 --emit-dag-isel 後端的計時結果。

===-------------------------------------------------------------------------===
                             TableGen Phase Timing
===-------------------------------------------------------------------------===
  Total Execution Time: 746.3868 seconds (747.1447 wall clock)

   ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---
  657.7938 ( 88.1%)   0.1404 ( 90.0%)  657.9342 ( 88.1%)  658.6497 ( 88.2%)  Emit matcher table
  70.2317 (  9.4%)   0.0000 (  0.0%)  70.2317 (  9.4%)  70.2700 (  9.4%)  Convert to matchers
  14.8825 (  2.0%)   0.0156 ( 10.0%)  14.8981 (  2.0%)  14.9009 (  2.0%)  Parse, build records
   2.1840 (  0.3%)   0.0000 (  0.0%)   2.1840 (  0.3%)   2.1791 (  0.3%)  Sort patterns
   1.1388 (  0.2%)   0.0000 (  0.0%)   1.1388 (  0.2%)   1.1401 (  0.2%)  Optimize matchers
   0.0000 (  0.0%)   0.0000 (  0.0%)   0.0000 (  0.0%)   0.0050 (  0.0%)  Write output
  746.2308 (100.0%)   0.1560 (100.0%)  746.3868 (100.0%)  747.1447 (100.0%)  Total

後端已分為四個階段,並分別計時。

如果要檢測後端,請參閱後端 DAGISelEmitter.cpp 並搜尋 Records.startTimer