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
的子類別為 BitRecTy
、BitsRecTy
、CodeRecTy
、DagRecTy
、IntRecTy
、ListRecTy
、RecordRecTy
和 StringRecTy
。其中一些類別具有其他成員,將在以下小節中描述。
所有 從 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
繼承的子類別為 UnsetInit
和 TypedInit
。
Init
實例可以使用 <<
運算子列印到輸出流。
警告
未指定具有相同底層類型和值(例如,兩個值為「Hello」的字串)的兩個獨立初始化值是由兩個 Init
表示還是共用相同的 Init
。
1.2.5.1 UnsetInit
¶
此類別是 Init
的子類別,表示未設定(未初始化)的值。靜態函數 get()
可用於取得此類型的單例 Init
。
1.2.5.2 TypedInit
¶
此類別是 Init
的子類別,充當表示特定值類型的類別的父類別(未設定值除外)。這些類別包括 BitInit
、BitsInit
、DagInit
、DefInit
、IntInit
、ListInit
和 StringInit
。(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 的新後端需要以下步驟。
為您的後端 C++ 檔案命名,例如
GenAddressModes
。編寫新的後端,使用
TableGenBackendSkeleton.cpp
檔案作為起點。確定哪個 TableGen 實例需要新的後端。Clang 有一個實例,LLVM 也有另一個實例。或者您可能正在建置自己的實例。
將您的後端 C++ 檔案新增到適當的
CMakeLists.txt
檔案,使其能夠被建置。將您的 C++ 檔案新增到系統中。
1.4 後端骨架¶
TableGenBackendSkeleton.cpp
檔案為編寫新的 TableGen 後端提供了一個骨架 C++ 翻譯單元。以下是有關此檔案的一些注意事項。
包含的列表是大多數後端所需的最少列表。
與所有 LLVM C++ 檔案一樣,它具有
using namespace llvm;
陳述式。它還有一個匿名命名空間,其中包含所有特定於檔案的資料結構定義,以及體現發射器資料成員和函數的類別。繼續GenAddressModes
範例,此類別被命名為AddressModesEmitter
。發射器類別的建構子接受
RecordKeeper
參考,通常命名為RK
。RecordKeeper
參考儲存在資料成員中,以便可以從中取得記錄。此資料成員通常命名為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
中的所有資訊。有大量函數採用某種形式的欄位名稱並回傳其值。一個函數 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
,並回傳 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);
可以使用 <<
運算子列印以下類別的實例:RecordKeeper
、Record
、RecTy
、RecordVal
和 Init
。
輔助函數 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_0
、anonymous_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
。