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
的子類別有 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
類型,並返回對應於 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
提供了一個骨架 C++ 轉譯單元,用於編寫新的 TableGen 後端。以下是一些關於該檔案的注意事項。
包含清單是大多數後端所需的最小清單。
與所有 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(
名稱)
返回指定名稱類別的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);
以下類別的實例可以使用 <<
運算子列印: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)
:列印訊息,後跟與指定記錄相關聯的原始程式碼行(請參閱記錄)。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
請注意,後端的所有時間都歸類在「後端整體」之下。
如果後端已針對計時進行檢測,則其處理過程會分為多個階段,並分別計時。這是針對 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
。