Pass Infrastructure基础架构(下)
Pass Infrastructure基礎架構(下)
pass注冊
PassRegistration該類在示例中簡要顯示了各種pass類型的定義 。該機制允許注冊pass類,以便可以在文本pass管道描述中創建它們 。注冊示例如下所示:
void registerMyPass() {
PassRegistration(“argument”, “description”);
}
? MyPass 是派生密碼類的名稱。
? “參數”是用于以文本格式引用過程的參數。
? “說明”是pass的簡短說明。
對于無法默認構造的pass,PassRegistration接受可選的第三個參數,該參數接受回調以創建pass:
void registerMyPass() {
PassRegistration(
“argument”, “description”,
-> std::unique_ptr {
std::unique_ptr p = std::make_unique(/options/);
/… non-trivial-logic to configure the pass …/;
return p;
});
}
例如,可以使用這種注冊變體來接受命令行參數的傳遞配置,并將其傳遞給pass構造函數。
注意:請確保該pass以不共享數據的方式是可復制構造的,因為 pass管理器 可能會創建該pass的副本以并行運行。
pass管道注冊
上面描述的是用于注冊特定派生pass類的機制。最重要的是,MLIR允許以類似的方式注冊自定義傳遞管道。這允許自定義管道以與傳遞相同的方式提供給mlir-opt之類的工具,這對于封裝常見的管道(例如“ -O1”傳遞系列)很有用。管道pass類似的機制注冊,形式為PassPipelineRegistration。與之相比PassRegistration,此類采用管道構造器形式的附加參數,該參數修改提供的參數OpPassManager。
void pipelineBuilder(OpPassManager &pm) {
pm.addPass(std::make_unique());
pm.addPass(std::make_unique());
}
void registerMyPasses() {
// Register an existing pipeline builder function.
PassPipelineRegistration<>(
“argument”, “description”, pipelineBuilder);
// Register an inline pipeline builder.
PassPipelineRegistration<>(
“argument”, “description”, [](OpPassManager &pm) {
pm.addPass(std::make_unique());
pm.addPass(std::make_unique());
});
}
文本傳遞管道規范
前面的部分詳細介紹了如何使用特定的參數和說明注冊pass和pass管道。一旦注冊,就可以使用它們從字符串描述中配置通道管理器。這對于mlir-opt從命令行配置過程管理器的工具,或作為利用動態過程管道的過程的選項的 工具尤其有用 。
為了支持描述傳遞管道的完整結構的能力,MLIR支持對傳遞管道的自定義文本描述。文字描述包括嵌套結構,運行的傳遞和傳遞管道的參數以及這些傳遞和管道的任何選項。文本管道定義為一系列名稱,每個名稱本身都可以遞歸包含嵌套的管道描述。該規范的語法如下:
pipeline ::= op-name ( pipeline-element (, pipeline-element)* )
pipeline-element ::= pipeline | (pass-name | pass-pipeline-name) options?
options ::= ‘{’ (key (’=’ value)?)+ ‘}’
? op-name
o 這對應于要繼續運行的算子的助記符名稱,例如func或module。
? pass-name | pass-pipeline-name
o 這對應于已注冊的pass或pass管道的參數,例如cse或canonicalize。
? options
o 選項是特定的鍵值對,代表pass或傳遞管道定義的選項,如 “實例特定的傳遞選項” 部分所述。有關文本管道中的用法示例,請參見本節。
例如,以下管道:
$ mlir-opt foo.mlir -cse -canonicalize -convert-std-to-llvm=‘use-bare-ptr-memref-call-conv=1’
也可以指定為(pass-pass-pipeline標志):
$ mlir-opt foo.mlir -pass-pipeline=‘func(cse,canonicalize),convert-std-to-llvm{use-bare-ptr-memref-call-conv=1}’
為了支持使用來回傳遞一個通向文本表示的傳遞 OpPassManager::printAsTextualPipeline(raw_ostream&),請重寫StringRef Pass::getArgument()以指定注冊傳遞時使用的參數。
聲明式pass規范
可以pass類似于算子的形式聲明性地指定pass的某些方面 。該規范簡化了定義通道時使用的幾種機制。它可用于生成過程注冊調用,定義樣板過程實用程序以及生成過程文檔。
考慮以下在C ++中指定的過程:
struct MyPass : PassWrapper<MyPass, OperationPass> {
MyPass() = default;
MyPass(const MyPass &) {}
…
// Specify any options.
Option option{
*this, “example-option”,
llvm:🆑:desc(“An example option”), llvm:🆑:init(true)};
ListOption<int64_t> listOption{
*this, “example-list”,
llvm:🆑:desc(“An example list option”), llvm:🆑:ZeroOrMore,
llvm:🆑:MiscFlags::CommaSeparated};
// Specify any statistics.
Statistic statistic{this, “example-statistic”, “An example statistic”};
};
/// Expose this pass to the outside world.
std::unique_ptr foo::createMyPass() {
return std::make_unique();
}
/// Register this pass.
void foo::registerMyPass() {
PassRegistration(“my-pass”, “My pass summary”);
}
此pass可以這樣聲明地指定:
def MyPass : Pass<“my-pass”, “ModuleOp”> {
let summary = “My Pass Summary”;
let description = [{
Here we can now give a much larger description of MyPass, including all of
its various constraints and behavior.
}];
// A constructor must be provided to specify how to create a default instance
// of MyPass.
let constructor = “foo::createMyPass()”;
// Specify any options.
let options = [
Option<“option”, “example-option”, “bool”, /default=/“true”,
“An example option”>,
ListOption<“listOption”, “example-list”, “int64_t”,
“An example list option”,
“llvm:🆑:ZeroOrMore, llvm:🆑:MiscFlags::CommaSeparated”>
];
// Specify any statistics.
let statistics = [
Statistic<“statistic”, “example-statistic”, “An example statistic”>
];
}
使用gen-pass-decls生成器,可以自動生成上面的大多數樣板。該生成器將一個-name參數作為輸入,該參數為正在生成的一組pass提供標簽。該生成器產生兩個輸出塊:
第一個是用于在全局注冊表中注冊聲明性傳遞的代碼塊。對于每次pass,生成器都會生成一個registerFooPasswhereFoo 是tablegen中指定的定義的名稱。它還會生成一個 registerGroupPasses,其中Group是pass-name輸入參數提供的標記,該標記會注冊所有存在的pass。
// gen-pass-decls -name=“Example”
#define GEN_PASS_REGISTRATION
#include “Passes.h.inc”
void registerMyPasses() {
// Register all of the passes.
registerExamplePasses();
// Register MyPass specifically.
registerMyPassPass();
}
第二個是每個通道的基類,其中包含與通道定義相關的大多數樣板。這些類以的形式命名 MyPassBase,其中MyPass是tablegen中傳遞定義的名稱。可以這樣更新原始的C ++傳遞定義:
/// Include the generated base pass class definitions.
#define GEN_PASS_CLASSES
#include “Passes.h.inc”
/// Define the main class as deriving from the generated base class.
struct MyPass : MyPassBase {
/// The explicit constructor is no longer explicitly necessary when defining
/// pass options and statistics, the base class takes care of that
/// automatically.
…
/// The definitions of the options and statistics are now generated within
/// the base class, but are accessible in the same way.
};
/// Expose this pass to the outside world.
std::unique_ptr foo::createMyPass() {
return std::make_unique();
}
使用gen-pass-doc生成器,可以生成每個pass的降價文檔。參閱 Passes.md 以獲取實際MLIRpass的示例輸出。
Tablegen規范
該Pass級用來啟動一個新的通行定義。此類將傳遞給屬性的注冊表參數以及與傳遞所算子的算子類型相對應的可選字符串作為參數。該類包含以下字段:
? summary
o 該pass的簡短摘要,用作注冊pass時的描述。
? description
o pass的更長,更詳細的描述。在生成pass文件時使用。
? dependentDialects
o 代表Dialect此過程的類的字符串列表可能會引入實體,屬性/算子/類型/等。
? constructor
o 用于創建pass的默認實例的代碼塊。
? options
o pass使用的pass選項列表。
? statistics
o pass使用的pass統計信息列表。
選項
選項可以passOption和ListOption類指定。該Option 班采用下列模板參數:
? C ++變量名稱
o 用于生成的選項變量的名稱。
? 論據
o 選項的參數名稱。
? 類型
o 選項的C ++類型。
? 默認值
o 默認選項值。
? 描述
o 選項的一行描述。
? 附加選項標志
o 一個字符串,其中包含構造選項所必需的任何其它選項。
def MyPass : Pass<“my-pass”> {
let options = [
Option<“option”, “example-option”, “bool”, /default=/“true”,
“An example option”>,
];
}
該ListOption班采取以下字段:
? C ++變量名稱
o 用于生成的選項變量的名稱。
? 論據
o 選項的參數名稱。
? 元素類型
o 列表元素的C ++類型。
? 描述
o 選項的一行描述。
? 附加選項標志
o 一個字符串,其中包含構造選項所必需的任何其它選項。
def MyPass : Pass<“my-pass”> {
let options = [
ListOption<“listOption”, “example-list”, “int64_t”,
“An example list option”,
“llvm:🆑:ZeroOrMore, llvm:🆑:MiscFlags::CommaSeparated”>
];
}
統計
可以pass來指定統計信息Statistic,該參數采用以下模板參數:
? C ++變量名稱
o 用于生成的統計變量的名稱。
? 顯示名稱
o 顯示統計信息時使用的名稱。
? 描述
o 統計信息的一行描述。
def MyPass : Pass<“my-pass”> {
let statistics = [
Statistic<“statistic”, “example-statistic”, “An example statistic”>
];
}
pass檢測
MLIRpass該類提供了一個可定制的框架,以pass過程執行和分析計算PassInstrumentation。此類提供了通向PassManager的鉤子,用于觀察各種事件:
? runBeforePipeline
o 該回調僅在執行傳遞管道(即傳遞管理器)之前運行。
? runAfterPipeline
o 成功執行傳遞管道之后,無論是否成功執行此回調。
? runBeforePass
o 該回調將在執行傳遞之前運行。
? runAfterPass
o 成功執行傳遞后立即運行此回調。如果執行此鉤子,runAfterPassFailed則不會。
? runAfterPassFailed
o 傳遞執行失敗后立即運行此回調。如果執行此鉤子,runAfterPass則不會。
? runBeforeAnalysis
o 該回調在計算分析之前運行。
? runAfterAnalysis
o 在計算分析后立即運行此回調。
PassInstrumentation實例可以 pass方法直接向PassManager實例注冊 addInstrumentation。添加到PassManager的工具以類似堆棧的方式運行,即最后一個執行runBefore鉤子的工具將是第一個執行相應runAfter鉤子的工具。PassInstrumentation 保證類的鉤子以線程安全的方式執行,因此不需要額外的同步。在下面的示例設備中,該設備對計算DominanceInfo分析的次數進行計數:
struct DominanceCounterInstrumentation : public PassInstrumentation {
/// The cumulative count of how many times dominance has been calculated.
unsigned &count;
DominanceCounterInstrumentation(unsigned &count) : count(count) {}
void runAfterAnalysis(llvm::StringRef, TypeID id, Operation *) override {
if (id == TypeID::get())
++count;
}
};
MLIRContext *ctx = …;
PassManager pm(ctx);
// Add the instrumentation to the pass manager.
unsigned domInfoCount;
pm.addInstrumentation(
std::make_unique(domInfoCount));
// Run the pass manager on a module operation.
ModuleOp m = …;
if (failed(pm.run(m)))
…
llvm::errs() << “DominanceInfo was computed " << domInfoCount << " times!\n”;
標準設備
MLIR利用pass工具框架提供一些有用的開發人員工具和實用程序。這些工具中的每一個均可直接用于MLIR Pass框架的所有用戶。
pass時間
PassTiming設備提供有關pass執行和分析計算的時序信息。這可以快速了解哪些通道花費了最多的時間來執行,以及通道對管道總執行時間的影響。用戶可以pass直接在PassManager上啟用此檢測enableTiming。也可以pass-pass-timing標志在mlir-opt中使用此工具。PassTiming設備為計時結果提供了幾種不同的顯示模式,下面分別介紹每種模式:
列表顯示模式
在這種模式下,結果顯示在一個列表中,該列表按總時間排序,每個pass/分析實例匯總為一個唯一的結果。此視圖有助于大致了解分析/pass最多花費的時間。此顯示模式可在mlir-opt via中使用 -pass-timing-display=list。
$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline=‘func(cse,canonicalize)’ -convert-std-to-llvm -pass-timing -pass-timing-display=list
=-------------------------------------------------------------------------=
… Pass execution timing report …
=-------------------------------------------------------------------------=
Total Execution Time: 0.0203 seconds
—Wall Time— — Name —
0.0047 ( 55.9%) Canonicalizer
0.0019 ( 22.2%) VerifierPass
0.0016 ( 18.5%) LLVMLoweringPass
0.0003 ( 3.4%) CSE
0.0002 ( 1.9%) (A) DominanceInfo
0.0084 (100.0%) Total
管道顯示模式
在這種模式下,結果將顯示在嵌套管道視圖中,該視圖鏡像在通道管理器中執行的內部通道管道。該視圖對于特定地了解流水線中哪個部分花費最多的時間很有用,并且還可用于識別何時使分析無效和重新計算。這是默認顯示模式。
$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline=‘func(cse,canonicalize)’ -convert-std-to-llvm -pass-timing
=-------------------------------------------------------------------------=
… Pass execution timing report …
=-------------------------------------------------------------------------=
Total Execution Time: 0.0249 seconds
—Wall Time— — Name —
0.0058 ( 70.8%) ‘func’ Pipeline
0.0004 ( 4.3%) CSE
0.0002 ( 2.6%) (A) DominanceInfo
0.0004 ( 4.8%) VerifierPass
0.0046 ( 55.4%) Canonicalizer
0.0005 ( 6.2%) VerifierPass
0.0005 ( 5.8%) VerifierPass
0.0014 ( 17.2%) LLVMLoweringPass
0.0005 ( 6.2%) VerifierPass
0.0082 (100.0%) Total
多線程傳遞時序
在pass管理器中啟用多線程后,顯示的含義會稍有變化。首先,添加一個新的計時列User Time,顯示所有線程花費的總時間。其次,該Wall Time 列顯示在所有線程中花費的最長的個人時間。這意味著該Wall Time列將繼續提供感知時間或時鐘時間的指示符,而User Time則將顯示總的cpu時間。
$ mlir-opt foo.mlir -pass-pipeline=‘func(cse,canonicalize)’ -convert-std-to-llvm -pass-timing
=-------------------------------------------------------------------------=
… Pass execution timing report …
=-------------------------------------------------------------------------=
Total Execution Time: 0.0078 seconds
—User Time— —Wall Time— — Name —
0.0177 ( 88.5%) 0.0057 ( 71.3%) ‘func’ Pipeline
0.0044 ( 22.0%) 0.0015 ( 18.9%) CSE
0.0029 ( 14.5%) 0.0012 ( 15.2%) (A) DominanceInfo
0.0038 ( 18.9%) 0.0015 ( 18.7%) VerifierPass
0.0089 ( 44.6%) 0.0025 ( 31.1%) Canonicalizer
0.0006 ( 3.0%) 0.0002 ( 2.6%) VerifierPass
0.0004 ( 2.2%) 0.0004 ( 5.4%) VerifierPass
0.0013 ( 6.5%) 0.0013 ( 16.3%) LLVMLoweringPass
0.0006 ( 2.8%) 0.0006 ( 7.0%) VerifierPass
0.0200 (100.0%) 0.0081 (100.0%) Total
IR打印
調試時,通常在傳遞管道的各個階段轉儲IR很有用。這是IR打印設備發揮作用的地方。pass選擇性地對正在執行的遍歷進行過濾,該設備可以在遍歷執行之前和之后有條件地打印IR。該工具可以passenableIRPrinting方法直接添加到PassManager 。mlir-opt提供了一些使用此工具的有用標志:
? print-ir-before=(comma-separated-pass-list)
o 在pass列表中提供的每個pass之前打印IR。
? print-ir-before-all
o 在管道中的每一次pass之前都要打印IR。
$ mlir-opt foo.mlir -pass-pipeline=‘func(cse)’ -print-ir-before=cse
*** IR Dump Before CSE ***
func @simple_constant() -> (i32, i32) {
%c1_i32 = constant 1 : i32
%c1_i32_0 = constant 1 : i32
return %c1_i32, %c1_i32_0 : i32, i32
}
? print-ir-after=(comma-separated-pass-list)
o 在pass列表中提供的每個pass之后,打印IR。
? print-ir-after-all
o 每次pass管道后,請打印IR。
$ mlir-opt foo.mlir -pass-pipeline=‘func(cse)’ -print-ir-after=cse
*** IR Dump After CSE ***
func @simple_constant() -> (i32, i32) {
%c1_i32 = constant 1 : i32
return %c1_i32, %c1_i32 : i32, i32
}
? print-ir-after-change
o 如果pass更改了IR,則僅在pass之后打印IR。這有助于減少“無趣”pass的IR轉儲數量。
o 注意:pass比較傳遞前后算子的哈希值來檢測更改。這增加了額外的運行時間來計算IR的哈希值,在極少數情況下,取決于所使用的哈希算法的沖突率,可能會導致假陽性。
o 注意:此選項應與上面的其它“以后打印”選項一起使用,因為僅此選項無法啟用打印。
$ mlir-opt foo.mlir -pass-pipeline=‘func(cse,cse)’ -print-ir-after=cse -print-ir-after-change
*** IR Dump After CSE ***
func @simple_constant() -> (i32, i32) {
%c1_i32 = constant 1 : i32
return %c1_i32, %c1_i32 : i32, i32
}
? print-ir-module-scope
o 無論pass類型或算子嵌套級別如何,始終打印頂層模塊算子。
o 注意:僅在禁用多線程(-mlir-disable-threading)時才應使用在模塊范圍內打印
$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline=‘func(cse)’ -print-ir-after=cse -print-ir-module-scope
*** IR Dump After CSE *** (‘func’ operation: @bar)
func @bar(%arg0: f32, %arg1: f32) -> f32 {
…
}
func @simple_constant() -> (i32, i32) {
%c1_i32 = constant 1 : i32
%c1_i32_0 = constant 1 : i32
return %c1_i32, %c1_i32_0 : i32, i32
}
*** IR Dump After CSE *** (‘func’ operation: @simple_constant)
func @bar(%arg0: f32, %arg1: f32) -> f32 {
…
}
func @simple_constant() -> (i32, i32) {
%c1_i32 = constant 1 : i32
return %c1_i32, %c1_i32 : i32, i32
}
崩潰和失敗復制
MLIR中的 pass管理器包含一個內置的機制,即使發生崩潰或pass失敗,也可以生成可復制的內容 。可以passPassManager::enableCrashReproducerGeneration或pass命令行標志 啟用此功能 pass-pipeline-crash-reproducer。在任何一種情況下,都提供一個參數,該參數對應于.mlir應將可復制文件寫入的輸出文件名。可復制的內容包含正在執行的過程管理器的配置,以及運行任何過程之前的初始IR。潛在的可復制性可能具有以下形式:
// configuration: -pass-pipeline=‘func(cse,canonicalize),inline’
// note: verifyPasses=false
module {
func @foo() {
…
}
}
本地復制器生成
可以向命令行傳遞一個附加標志PassManager::enableCrashReproducerGeneration,并passpass-pipeline-local-reproducer命令行指定 該附加標志 ,以表明pass管理器應嘗試生成“本地”再現器。這將嘗試在pass失敗之前立即生成包含IR的復制器。這對于已知崩潰是在特定遍內或原始輸入依賴于可能并不總是可用的成分(如語言或遍)的情況很有用。
例如,如果先前示例中的失敗來自canonicalize,則將生成以下復制器:
// configuration: -pass-pipeline=‘func(canonicalize)’
// note: verifyPasses=false
module {
func @foo() {
…
}
}
總結
以上是生活随笔為你收集整理的Pass Infrastructure基础架构(下)的全部內容,希望文章能夠幫你解決所遇到的問題。