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 解析和收集的所有類別和記錄的容器。RecordKeeper 實例在後端由 TableGen 呼叫時傳遞給後端。此類別通常縮寫為 RK

記錄保管者中有兩個映射,一個用於類別,另一個用於記錄(後者通常稱為 defs)。每個映射將類別或記錄名稱映射到 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 type,並傳回與 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 檔案為編寫新的 TableGen 後端提供了一個骨架 C++ 翻譯單元。以下是有關此檔案的一些注意事項。

  • 包含的列表是大多數後端所需的最少列表。

  • 與所有 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(name) 會回傳具名類別的 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 中的所有資訊。有大量函數採用某種形式的欄位名稱並回傳其值。一個函數 getValueInitInit * 形式回傳值。另一個函數 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,並回傳 std::pair 對組陣列的 ArrayRef。超類別以後序排列:將超類別的欄位複製到記錄中時,訪問超類別的順序。每對組由指向超類別記錄的 Record 實例的指標和 SMRange 類別的實例組成。範圍指示類別定義的開始和結束的原始檔位置。

此範例取得 Prototype 記錄的超類別,然後迭代回傳陣列中的對組。

ArrayRef<std::pair<const 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):列印訊息,後接與指定記錄相關聯的原始碼行(請參閱 Record)。

  • 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

請注意,後端的所有時間都歸在「Backend overall」下。

如果後端已針對計時進行檢測,則其處理會分為多個階段,每個階段分別計時。這是針對在 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