【從零開(kāi)始學(xué)深度學(xué)習(xí)編譯器】十八,MLIR中的Interfaces
0x0. 前言
這篇文章用來(lái)了解一下MLIR中的Interfaces(接口)。MLIR是一個(gè)通用可擴(kuò)展的框架,由不同層次的具有 特定屬性,Operation以及Type的Dialects構(gòu)成。正是由于Dialects的分層設(shè)計(jì), 使得MLIR可以表達(dá)多種語(yǔ)意和抽象級(jí)別的Operation。但這個(gè)分級(jí)設(shè)計(jì)也存在一個(gè)缺點(diǎn),那就是在不同的Dialect層次進(jìn)行Operation轉(zhuǎn)換或者做變換(Pass)的時(shí)候我們需要明確每個(gè)Dialect下的每個(gè)Operation的具體語(yǔ)意,否則就可能會(huì)轉(zhuǎn)換或變換失敗。其實(shí)基于MLIR開(kāi)發(fā)過(guò)的讀者應(yīng)該碰到過(guò)組合一些MLIR Pass對(duì)一個(gè)MLIR文件進(jìn)行Lower的時(shí)候,有可能出現(xiàn)Op轉(zhuǎn)換失敗的情況。為了緩解這種情況,MLIR提出了Interfaces。實(shí)際上在【從零開(kāi)始學(xué)深度學(xué)習(xí)編譯器】十三,如何在MLIR里面寫(xiě)Pass? 這里我們已經(jīng)利用過(guò)Interfaces來(lái)實(shí)現(xiàn)內(nèi)聯(lián)以及形狀推導(dǎo)Pass了。這一節(jié)就更深入的了解一下MLIR中的Interfaces,最后還結(jié)合了OneFlow IR中的UserOpCompatibleInterface例子來(lái)進(jìn)一步加深了解。
?本文提到的Operation和操作是一個(gè)東西,都是MLIR Dialect下的操作。
?
0x1. 動(dòng)機(jī)
Interfaces可以翻譯成接口,MLIR的Interfaces提供了和IR交互的通用方式。Interfaces的設(shè)計(jì)目標(biāo)是可以不用侵入到具體某個(gè)Dialect下的特定Operation和Dialect的特定知識(shí)就達(dá)到可以轉(zhuǎn)換和分析MLIR表達(dá)式。這樣就可以將轉(zhuǎn)換,分析和新增一個(gè)Dialect和對(duì)應(yīng)的Operation 進(jìn)行解耦,大大增強(qiáng)MLIR的可擴(kuò)展性。
0x2. Dialect Interfaces定義(細(xì)看)
Dialect Interfaces一般用在想對(duì)一組屬性,Operation,類型進(jìn)行通用的轉(zhuǎn)換(Pass)或者分析,這些屬性,Operation,類型可以是由不同的Dialect定義的。這些Interfaces一般會(huì)廣泛覆蓋各個(gè)級(jí)別的Dialects,僅用于少數(shù)分析和變換。因此,我們要明確Interface并不是Operation的核心,而是一些通用變換的核心。在【從零開(kāi)始學(xué)深度學(xué)習(xí)編譯器】十三,如何在MLIR里面寫(xiě)Pass? 這里有一個(gè)使用內(nèi)聯(lián)Interface實(shí)現(xiàn)內(nèi)聯(lián)Pass的例子。內(nèi)聯(lián)通常查詢的是有關(guān)Dialect中Operation的高級(jí)信息,例如cost modeling和合法性,而這些信息通常不特定于某個(gè)Dialect下的某個(gè)Operation單獨(dú)存在。
Dialect Interface可以通過(guò)繼承一個(gè)CRTP基類DialectInterfaceBase::Base<>來(lái)進(jìn)行定義。CRTP的介紹可以參考:https://zh.wikipedia.org/wiki/奇異遞歸模板模式,我理解靜態(tài)多態(tài)(CRTP)是因?yàn)镸LIR里面會(huì)存在很多Dialect Interface要從這個(gè)DialectInterfaceBase::Base<>基類派生出來(lái),為了性能考慮用CRTP比較合適。這個(gè)基類提供了Dialect Interface注冊(cè)必須的一些接口,方便將來(lái)引用它們。當(dāng)Interface被定義之后,Dialects就可以使用Dialect特定信息去重寫(xiě)它。被一個(gè)Dialect定義的Interfaces通過(guò)addInterfaces<>進(jìn)行注冊(cè),和屬性,Operation,Type的注冊(cè)機(jī)制類似。下面舉一個(gè)栗子:
//?定義一個(gè)基礎(chǔ)的內(nèi)聯(lián)Interface類以允許Dialect選擇加入內(nèi)聯(lián)。?
class?DialectInlinerInterface?:
????public?DialectInterface::Base?{
public:
??///?如果給定的區(qū)域?'src'?可以內(nèi)聯(lián)到該區(qū)域中,則返回 true。
??///?'dest'?附加到注冊(cè)到當(dāng)前Dialect的Operation上。?
??///?'valueMapping'?包含來(lái)自?'src'?區(qū)域內(nèi)的任何重新映射的值。?
??///?例如,這可用于檢查哪些值將替換“src”區(qū)域中的條目參數(shù)。?
??virtual?bool?isLegalToInline(Region?*dest,?Region?*src,
???????????????????????????????BlockAndValueMapping?&valueMapping)?const?{
????return?false;
??}
};
///?覆蓋內(nèi)聯(lián)接口以添加對(duì) AffineDialect 的支持以啟用內(nèi)聯(lián)Affine Dialect的Operation。?
struct?AffineInlinerInterface?:?public?DialectInlinerInterface?{
??/// Affine結(jié)構(gòu)具有特定的內(nèi)聯(lián)約束。?
??bool?isLegalToInline(Region?*dest,?Region?*src,
???????????????????????BlockAndValueMapping?&valueMapping)?const?final?{
????...
??}
};
///?在Dialect下注冊(cè)內(nèi)聯(lián)Interfaces
AffineDialect::AffineDialect(MLIRContext?*context)?...?{
??addInterfaces();
}
這些Interfaces被注冊(cè)之后,在執(zhí)行MLIR的變換和分析時(shí)就可以從Dialect中查到,并不需要確定特定的Dialect子類(如具體到某個(gè)Operation)。例如:
Dialect?*dialect?=?...;
if?(DialectInlinerInterface?*interface
??????=?dialect->getRegisteredInterface())?{
??//?Dialect提供了這個(gè)Interface的實(shí)現(xiàn)
??...
}
例如,在llvm/mlir/lib/IR/Dialect.cpp這個(gè)文件中的registerDelayedInterfaces函數(shù)就展示了上面這種用法,這個(gè)函數(shù)用于注冊(cè)加載進(jìn)來(lái)的Dialect的Interfaces:
void?DialectRegistry::registerDelayedInterfaces(Dialect?*dialect)?const?{
??auto?it?=?interfaces.find(dialect->getTypeID());
??if?(it?==?interfaces.end())
????return;
??//?Add?an?interface?if?it?is?not?already?present.
??for?(const?auto?&kvp?:?it->getSecond().dialectInterfaces)?{
????if?(dialect->getRegisteredInterface(kvp.first))
??????continue;
????dialect->addInterface(kvp.second(dialect));
??}
??//?Add?attribute,?operation?and?type?interfaces.
??for?(const?auto?&info?:?it->getSecond().objectInterfaces)
????std::get<2>(info)(dialect->getContext());
}
0x3. DialectInterfaceCollection(選看,我還沒(méi)用過(guò))
DialectInterfaceCollection提供了一個(gè)額外的實(shí)用程序。這個(gè)類允許收集已在 MLIRContext 實(shí)例中注冊(cè)給定Interface的所有Dialect。這對(duì)于隱藏和優(yōu)化已注冊(cè)Dialect Interface的查找很有用。
class?InlinerInterface?:?public
????DialectInterfaceCollection?{
??//?此類的鉤子是DialectInlinerInterface的鉤子鏡像,默認(rèn)實(shí)現(xiàn)為調(diào)用給定Dialect上Interface上的鉤子。?
??virtual?bool?isLegalToInline(Region?*dest,?Region?*src,
???????????????????????????????BlockAndValueMapping?&valueMapping)?const?{
????auto?*handler?=?getInterfaceFor(dest->getContainingOp());
????return?handler???handler->isLegalToInline(dest,?src,?valueMapping)?:?false;
??}
};
MLIRContext?*ctx?=?...;
InlinerInterface?interface(ctx);
if(!interface.isLegalToInline(...))
???...
0x4. 屬性,操作,類型 Interfaces(選看)
顧名思義,屬性/操作/類型Interface是在特定屬性/操作/類型級(jí)別注冊(cè)的那些。這些Interface 「通過(guò)提供」必須實(shí)現(xiàn)的虛接口來(lái)提供對(duì)派生對(duì)象的訪問(wèn)。例如,許多分析和轉(zhuǎn)換想要知道Operation的副作用以提高性能和正確性。Operation的副作用通常與特定Operation的語(yǔ)義相關(guān),例如 affine.load Operation具有讀取效果(顧名思義)。
這些Interface是通過(guò)覆蓋特定 IR 實(shí)體的 CRTP 類來(lái)定義的;分別是 AttrInterface、OpInterface 或 TypeInterface。這些類將定義Concept和Model類的 Traits 類作為模板參數(shù)。這些類提供了基于概念的多態(tài)性的實(shí)現(xiàn),其中Concept定義了一組虛方法,這些方法被在具體實(shí)體類型上模板化的Model覆蓋。需要注意的是,這些類應(yīng)該是純的,不應(yīng)包含非靜態(tài)數(shù)據(jù)成員或其他可變數(shù)據(jù)。為了將Interface附加到對(duì)象,基類提供了一個(gè)可以附加到該對(duì)象的特征列表的 Trait 類(跳過(guò)下面的示例代碼就可以看到解釋)。
struct?ExampleOpInterfaceTraits?{
??///?定義一個(gè)基礎(chǔ)Concept類,指定要實(shí)現(xiàn)的虛擬接口。?
??struct?Concept?{
????virtual?~Concept();
????///?這是對(duì)Operation的非靜態(tài)鉤子的示例?
????virtual?unsigned?exampleInterfaceHook(Operation?*op)?const?=?0;
????///?這是Operation的靜態(tài)鉤子示例。?靜態(tài)鉤子不需要Operation的具體實(shí)例。?實(shí)現(xiàn)是一個(gè)虛擬的鉤子,和非靜態(tài)的情況一樣,因?yàn)殂^??子本身的實(shí)現(xiàn)還是需要間接實(shí)現(xiàn)的。?
????virtual?unsigned?exampleStaticInterfaceHook()?const?=?0;
??};
??///?為給定Operation類型的concept定義一個(gè)model類?
??template?<typename?ConcreteOp>
??struct?Model?:?public?Concept?{
????///?覆蓋要在具體Operation上分發(fā)的方法?
????unsigned?exampleInterfaceHook(Operation?*op)?const?final?{
??????return?llvm::cast(op).exampleInterfaceHook();
????}
????///?覆蓋要在具體Operation上分發(fā)的方法
????unsigned?exampleStaticInterfaceHook()?const?final?{
??????return?ConcreteOp::exampleStaticInterfaceHook();
????}
??};
};
///?定義分析和轉(zhuǎn)換將與之交互的主Interface類。?
class?ExampleOpInterface?:?public?OpInterface??????????????????????????????????????????????ExampleOpInterfaceTraits>?{
public:
??///?繼承基類構(gòu)造函數(shù)以支持 LLVM 樣式的轉(zhuǎn)換。?
??using?OpInterface::OpInterface;
??/// Interface分發(fā)到`getImpl()`,這是一個(gè)由基本的`OpInterface`類提供的方法,它返回concept的一個(gè)實(shí)例。?
??unsigned?exampleInterfaceHook()?const?{
????return?getImpl()->exampleInterfaceHook(getOperation());
??}
??unsigned?exampleStaticInterfaceHook()?const?{
????return?getImpl()->exampleStaticInterfaceHook(getOperation()->getName());
??}
};
一旦定義了Interface,就可以通過(guò)添加提供的特征 ExampleOpInterface::Trait 將其注冊(cè)到操作中,如前所述。使用此接口就像使用任何其他派生操作類型,即強(qiáng)制轉(zhuǎn)換:
///?定義Operation時(shí),Interface通過(guò)`OpInterface<>`基類提供的嵌套`Trait`類進(jìn)行注冊(cè)。
class?MyOp?:?public?Op?{
public:
??///?The?definition?of?the?interface?method?on?the?derived?operation.
??unsigned?exampleInterfaceHook()?{?return?...;?}
??static?unsigned?exampleStaticInterfaceHook()?{?return?...;?}
};
///?稍后,我們可以查詢特定Operation(如“MyOp”)是否覆蓋給定Interface。?
Operation?*op?=?...;
if?(ExampleOpInterface?example?=?dyn_cast(op))
??llvm::errs()?<"hook?returned?=?"?<"\n";
如果你讀到這里并且看過(guò)之前在【從零開(kāi)始學(xué)深度學(xué)習(xí)編譯器】十三,如何在MLIR里面寫(xiě)Pass? 使用內(nèi)聯(lián)Interface的例子,相信可以更加理解在Toy Dialect下注冊(cè)內(nèi)聯(lián)Pass的幾個(gè)步驟。
0x5. 屬性、操作和類型Interfaces的外部Model(選看)
這可能需要為 IR 對(duì)象提供Interface實(shí)現(xiàn)而不修改所述對(duì)象的定義。值得注意的是,這允許在定義它們的Dialect之外實(shí)現(xiàn)屬性、操作和類型的Interface,例如,為built-in類型提供InterFace。這是通過(guò)使用兩個(gè)基于concept的多態(tài)性Model從 Concept 派生的擴(kuò)展類來(lái)實(shí)現(xiàn)的,如下所示(注意注釋):
struct?ExampleTypeInterfaceTraits?{
??struct?Concept?{
????virtual?unsigned?exampleInterfaceHook(Type?type)?const?=?0;
????virtual?unsigned?exampleStaticInterfaceHook()?const?=?0;
??};
??template?<typename?ConcreteType>
??struct?Model?:?public?Concept?{?/*...*/?};
??///?與?Model?不同,F(xiàn)allbackModel?將類型對(duì)象傳遞給
??///?鉤子,使其在方法體中可訪問(wèn),即使該方法未在類本身中定義,
??///?因此沒(méi)有“this”訪問(wèn)權(quán)限。ODS 自動(dòng)為所有Interfaces生成此類。?
??template?<typename?ConcreteType>
??struct?FallbackModel?:?public?Concept?{
????unsigned?exampleInterfaceHook(Type?type)?const?override?{
??????getImpl()->exampleInterfaceHook(type);
????}
????unsigned?exampleStaticInterfaceHook()?const?override?{
??????ConcreteType::exampleStaticInterfaceHook();
????}
??};
??///?`ExternalModel`?通過(guò)顯式地分離實(shí)現(xiàn)Interface的模型類和實(shí)現(xiàn)Interface的類型類,為Interface方法的默認(rèn)實(shí)現(xiàn)提供了一個(gè)位置。?然后可以使用?`cast`?定義默認(rèn)實(shí)現(xiàn)。?如果`ConcreteType`?沒(méi)有提供默認(rèn)實(shí)現(xiàn)所需的API,自定義實(shí)現(xiàn)可以直接使用`FallbackModel`?來(lái)覆蓋默認(rèn)實(shí)現(xiàn)。位于類模板中,它永遠(yuǎn)不會(huì)被實(shí)例化,也不會(huì)導(dǎo)致編譯錯(cuò)誤。ODS 自動(dòng)生成此類并將默認(rèn)方法實(shí)現(xiàn)放入其中。?
??template?<typename?ConcreteModel,?typename?ConcreteType>
??struct?ExternalModel?:?public?FallbackModel?{
????unsigned?exampleInterfaceHook(Type?type)?const?override?{
??????//?Default?implementation?can?be?provided?here.
??????return?type.cast().callSomeTypeSpecificMethod();
????}
??};
};
通過(guò)派生 FallbackMode 或 ExternalModel 并通過(guò)在給定context中向相關(guān)類注冊(cè)Model類,可以為屬性、操作和類型接口提供外部Models。除非注冊(cè),否則其它c(diǎn)ontext將看不到該Interface。例如:
///?具體類的外部Interface實(shí)現(xiàn)。?這不需要修改類型類本身的定義。?
struct?ExternalModelExample
????:?public?ExampleTypeInterface::ExternalModel?????????????????????????????????????????????????IntegerType>?{
??static?unsigned?exampleStaticInterfaceHook()?{
????//?實(shí)現(xiàn)被提供在這里
????return?IntegerType::someStaticMethod();
??}
??//?無(wú)需定義在“ExternalModel”中具有默認(rèn)實(shí)現(xiàn)的“exampleInterfaceHook”。?但如果需要,它可以被覆蓋。?
}
int?main()?{
??MLIRContext?context;
??/*?...?*/;
??//?在使用之前,將interface model附加到給定context中的類型。?預(yù)計(jì)此時(shí)已加載包含該類型的Dialect。?
??IntegerType::attachInterface(context);
}
文檔最后還提出了一個(gè)建議,即當(dāng)我們“擁有”外部應(yīng)用的Interface時(shí)才使用此機(jī)制。這可以防止包含對(duì)象的Dialect的所有者和interface的所有者都不知道Interface實(shí)現(xiàn)的情況,這可能導(dǎo)致重復(fù)或發(fā)散的實(shí)現(xiàn)。還沒(méi)有碰到過(guò)需要使用這種機(jī)制的情況,這里不繼續(xù)深入了。
0x6. OpInterface的Dialect Fallback(選看)
一些Dialects有一個(gè)開(kāi)放的生態(tài)系統(tǒng),并沒(méi)有注冊(cè)所有可能的Operation。在這種情況下,仍然可以為實(shí)現(xiàn)這些操作的 OpInterface 提供支持。當(dāng)操作未注冊(cè)或未提供Interface實(shí)現(xiàn)時(shí),查詢將fallback到Dialect本身。
第二種Model用于此類情況,并在ODS使用名為 FallbackModel(見(jiàn)下文)時(shí)自動(dòng)生成??梢詾樘囟ǚ窖詫?shí)現(xiàn)此Model:
//?This?is?the?implementation?of?a?dialect?fallback?for?`ExampleOpInterface`.
struct?FallbackExampleOpInterface
????:?public?ExampleOpInterface::FallbackModel<
??????????FallbackExampleOpInterface>?{
??static?bool?classof(Operation?*op)?{?return?true;?}
??unsigned?exampleInterfaceHook(Operation?*op)?const;
??unsigned?exampleStaticInterfaceHook()?const;
};
然后,Dialect可以實(shí)例化此實(shí)現(xiàn),并通過(guò)覆蓋 getRegisteredInterfaceForOp 方法在特定Operation上返回它:
void?*TestDialect::getRegisteredInterfaceForOp(TypeID?typeID,
???????????????????????????????????????????????StringAttr?opName)?{
??if?(typeID?==?TypeID::get())?{
????if?(isSupported(opName))
??????return?fallbackExampleOpInterface;
????return?nullptr;
??}
??return?nullptr;
}
這一節(jié)也不是很懂,先記錄著吧。通過(guò)上面對(duì)Interfaces的介紹,我們能留下一些基礎(chǔ)的印象我覺(jué)得應(yīng)該就夠了,接下來(lái)要講的是如何基于ODS去定義Interfaces,這才是我們這篇文章的重點(diǎn)。
0x7. 利用ODS框架定義Interface(重要)
如上所述,Interface允許屬性、Operation和Type 暴露調(diào)用方法,而無(wú)需調(diào)用者知道特定的派生類型。這種基礎(chǔ)設(shè)施的缺點(diǎn)是它需要一些樣板才能將所有部分連接在一起。MLIR 提供了一種機(jī)制,用于在 ODS 中以聲明方式定義接口,并自動(dòng)生成 C++ 定義。舉個(gè)栗子:
def?ExampleOpInterface?:?OpInterface<"ExampleOpInterface">?{
??let?description?=?[{
????This?is?an?example?interface?definition.
??}];
??let?methods?=?[
????InterfaceMethod<
??????"This?is?an?example?of?a?non-static?hook?to?an?operation.",
??????"unsigned",?"exampleInterfaceHook"
????>,
????StaticInterfaceMethod<
??????"This?is?an?example?of?a?static?hook?to?an?operation.",
??????"unsigned",?"exampleStaticInterfaceHook"
????>,
??];
}
提供 AttrInterface、OpInterface 或 TypeInterface 類的定義將自動(dòng)生成接口的 C++ 類。接口由以下組件組成:
C++ 類名(通過(guò)模板參數(shù)提供) 。 描述。( description)。C++ Namespace。( cppNamespace)。即Interface類應(yīng)該產(chǎn)生在哪個(gè)C++名稱空間下。Methods( methods)。由 IR 對(duì)象定義的Interfaces鉤子方法列表。Extra Class Declarations。可選的: extraClassDeclaration。在Interface類的聲明中生成的附加 C++ 代碼。這允許在面向用戶的Interface類上定義方法等,不需要鉤到 IR 實(shí)體。這些聲明在接口方法的默認(rèn)實(shí)現(xiàn)中不是隱式可見(jiàn)的,但可以使用全名限定訪問(wèn)靜態(tài)聲明。
OpInterface類可能還額外包含Verifier(verify)。它是一個(gè)包含附加驗(yàn)證的 C++ 代碼塊應(yīng)用于Interface所附加的Operation。此代碼塊的結(jié)構(gòu)與 Trait::verifyTrait 方法的結(jié)構(gòu) 1-1 對(duì)應(yīng)。
有兩種類型的方法可以與Interface一起使用,InterfaceMethod和 StaticInterfaceMethod。它們都由相同的核心組件組成,區(qū)別在于 StaticInterfaceMethod 為派生的 IR 對(duì)象上的靜態(tài)方法建模。Interface 方法有以下組件:
Description:方法的描述信息,一個(gè)字符串。 ReturnType:與方法的 C++ 返回類型對(duì)應(yīng)的字符串。 MethodName:與方法的 C++ 名稱對(duì)應(yīng)的字符串。 Arguments (Optional):分別對(duì)應(yīng)于 C++ 類型和變量名稱的字符串。 MethodBody (Optional)和DefaultImplementation。暫沒(méi)有用到,以后有需要再查。
如果Operation使用 DeclareOpInterfaceMethods 指定Interface,則 ODS 還允許為Operation的 InterfaceMethods 生成聲明(請(qǐng)參見(jiàn)下面的示例)。
ef?MyInterface?:?OpInterface<"MyInterface">?{
??let?description?=?[{
????This?is?the?description?of?the?interface.?It?provides?concrete?information
????on?the?semantics?of?the?interface,?and?how?it?may?be?used?by?the?compiler.
??}];
??let?methods?=?[
????InterfaceMethod<[{
??????This?method?represents?a?simple?non-static?interface?method?with?no
??????inputs,?and?a?void?return?type.?This?method?is?required?to?be?implemented
??????by?all?operations?implementing?this?interface.?This?method?roughly
??????correlates?to?the?following?on?an?operation?implementing?this?interface:
??????```c++
??????class?ConcreteOp?...?{
??????public:
????????void?nonStaticMethod();
??????};
??????```
????}],?"void",?"nonStaticMethod"
????>,
????InterfaceMethod<[{
??????This?method?represents?a?non-static?interface?method?with?a?non-void
??????return?value,?as?well?as?an?`unsigned`?input?named?`i`.?This?method?is
??????required?to?be?implemented?by?all?operations?implementing?this?interface.
??????This?method?roughly?correlates?to?the?following?on?an?operation
??????implementing?this?interface:
??????```c++
??????class?ConcreteOp?...?{
??????public:
????????Value?nonStaticMethod(unsigned?i);
??????};
??????```
????}],?"Value",?"nonStaticMethodWithParams",?(ins?"unsigned":$i)
????>,
????StaticInterfaceMethod<[{
??????This?method?represents?a?static?interface?method?with?no?inputs,?and?a
??????void?return?type.?This?method?is?required?to?be?implemented?by?all
??????operations?implementing?this?interface.?This?method?roughly?correlates
??????to?the?following?on?an?operation?implementing?this?interface:
??????```c++
??????class?ConcreteOp?...?{
??????public:
????????static?void?staticMethod();
??????};
??????```
????}],?"void",?"staticMethod"
????>,
????StaticInterfaceMethod<[{
??????This?method?corresponds?to?a?static?interface?method?that?has?an?explicit
??????implementation?of?the?method?body.?Given?that?the?method?body?has?been
??????explicitly?implemented,?this?method?should?not?be?defined?by?the?operation
??????implementing?this?method.?This?method?merely?takes?advantage?of?properties
??????already?available?on?the?operation,?in?this?case?its?`build`?methods.?This
??????method?roughly?correlates?to?the?following?on?the?interface?`Model`?class:
??????```c++
??????struct?InterfaceTraits?{
????????///?...?The?`Concept`?class?is?elided?here?...
????????template?<typename?ConcreteOp>
????????struct?Model?:?public?Concept?{
??????????Operation?*create(OpBuilder?&builder,?Location?loc)?const?override?{
????????????return?builder.create(loc);
??????????}
????????}
??????};
??????```
??????Note?above?how?no?modification?is?required?for?operations?implementing?an
??????interface?with?this?method.
????}],
??????"Operation?*",?"create",?(ins?"OpBuilder?&":$builder,?"Location":$loc),
??????/*methodBody=*/[{
????????return?builder.create(loc);
????}]>,
????InterfaceMethod<[{
??????This?method?represents?a?non-static?method?that?has?an?explicit
??????implementation?of?the?method?body.?Given?that?the?method?body?has?been
??????explicitly?implemented,?this?method?should?not?be?defined?by?the?operation
??????implementing?this?method.?This?method?merely?takes?advantage?of?properties
??????already?available?on?the?operation,?in?this?case?its?`build`?methods.?This
??????method?roughly?correlates?to?the?following?on?the?interface?`Model`?class:
??????```c++
??????struct?InterfaceTraits?{
????????///?...?The?`Concept`?class?is?elided?here?...
????????template?<typename?ConcreteOp>
????????struct?Model?:?public?Concept?{
??????????Operation?*create(Operation?*opaqueOp,?OpBuilder?&builder,
????????????????????????????Location?loc)?const?override?{
????????????ConcreteOp?op?=?cast(opaqueOp);
????????????return?op.getNumInputs()?+?op.getNumOutputs();
??????????}
????????}
??????};
??????```
??????Note?above?how?no?modification?is?required?for?operations?implementing?an
??????interface?with?this?method.
????}],
??????"unsigned",?"getNumInputsAndOutputs",?(ins),?/*methodBody=*/[{
????????return?$_op.getNumInputs()?+?$_op.getNumOutputs();
????}]>,
????InterfaceMethod<[{
??????This?method?represents?a?non-static?method?that?has?a?default
??????implementation?of?the?method?body.?This?means?that?the?implementation
??????defined?here?will?be?placed?in?the?trait?class?that?is?attached?to?every
??????operation?that?implements?this?interface.?This?has?no?effect?on?the
??????generated?`Concept`?and?`Model`?class.?This?method?roughly?correlates?to
??????the?following?on?the?interface?`Trait`?class:
??????```c++
??????template?<typename?ConcreteOp>
??????class?MyTrait?:?public?OpTrait::TraitBase?{
??????public:
????????bool?isSafeToTransform()?{
??????????ConcreteOp?op?=?cast(this->getOperation());
??????????return?op.getNumInputs()?+?op.getNumOutputs();
????????}
??????};
??????```
??????As?detailed?in?[Traits](Traits.md),?given?that?each?operation?implementing
??????this?interface?will?also?add?the?interface?trait,?the?methods?on?this
??????interface?are?inherited?by?the?derived?operation.?This?allows?for
??????injecting?a?default?implementation?of?this?method?into?each?operation?that
??????implements?this?interface,?without?changing?the?interface?class?itself.?If
??????an?operation?wants?to?override?this?default?implementation,?it?merely
??????needs?to?implement?the?method?and?the?derived?implementation?will?be
??????picked?up?transparently?by?the?interface?class.
??????```c++
??????class?ConcreteOp?...?{
??????public:
????????bool?isSafeToTransform()?{
??????????//?Here?we?can?override?the?default?implementation?of?the?hook
??????????//?provided?by?the?trait.
????????}
??????};
??????```
????}],
??????"bool",?"isSafeToTransform",?(ins),?/*methodBody=*/[{}],
??????/*defaultImplementation=*/[{
????}]>,
??];
}
//?Operation?interfaces?can?optionally?be?wrapped?inside
//?DeclareOpInterfaceMethods.?This?would?result?in?autogenerating?declarations
//?for?members?`foo`,?`bar`?and?`fooStatic`.?Methods?with?bodies?are?not
//?declared?inside?the?op?declaration?but?instead?handled?by?the?op?interface
//?trait?directly.
def?OpWithInferTypeInterfaceOp?:?Op<...
????[DeclareOpInterfaceMethods]>?{?...?}
//?Methods?that?have?a?default?implementation?do?not?have?declarations
//?generated.?If?an?operation?wishes?to?override?the?default?behavior,?it?can
//?explicitly?specify?the?method?that?it?wishes?to?override.?This?will?force
//?the?generation?of?a?declaration?for?those?methods.
def?OpWithOverrideInferTypeInterfaceOp?:?Op<...
????[DeclareOpInterfaceMethods"getNumWithDefault"]>]>?{?...?}
注意:在 ODS 框架中可以通過(guò) OpInterfaceTrait 類訪問(wèn) C++ 中定義的現(xiàn)有Operation接口。
0x8. Operation Interface列表
MLIR包括提供可能在許多不同Operation中通用的功能的標(biāo)準(zhǔn)Interface。下面是一些可以被任何方言直接使用的關(guān)鍵Interface的列表。每個(gè)Interface部分的標(biāo)題格式如下:
Interface class name( C++ class–ODS class(if applicable))
標(biāo)準(zhǔn)Interface截圖如下:

0x9. OneFlow的Interface
接下來(lái)我們以O(shè)neFlow IR為例,來(lái)看一下OneFlow Dialect中定義了哪些Interface。代碼在:oneflow/ir/include/OneFlow/OneFlowInterfaces.td 這里。OneFlow基于ODS定義了UserOpCompatibleInterface,ControlEdgeCompatibleInterface,NoGrad 等Interface類型。我們以UserOpCompatibleInterface為例來(lái)看一下它的實(shí)現(xiàn):
def?UserOpCompatibleInterface?:?OpInterface<"UserOpCompatible">?{
??let?description?=?[{
????Interface?to?getting?the?hard-coded?bn
??}];
??let?methods?=?[
????StaticInterfaceMethod<"",
????????"const?std::vector*" ,?"inputKeys",?(ins),?[{
????????static?std::vector<std::string>?val(mlir::oneflow::support::GetInputKeys(ConcreteOp::getOperationName().split('.').second.str()));
????????return?&val;
????}]>,
????StaticInterfaceMethod<"",
????????"const?std::vector*" ,?"outputKeys",?(ins),?[{
????????static?std::vector<std::string>?val(mlir::oneflow::support::GetOutputKeys(ConcreteOp::getOperationName().split('.').second.str()));
????????return?&val;
????}]>,
????InterfaceMethod<"",
????????"std::pair" ,?"getODSOperandIndexAndLength",?(ins?"unsigned":$index),?[{
????????return?$_op.getODSOperandIndexAndLength(index);
????}]>,
????InterfaceMethod<"",
????????"std::pair" ,?"getODSResultIndexAndLength",?(ins?"unsigned":$index),?[{
????????return?$_op.getODSResultIndexAndLength(index);
????}]>
??];
}
可以看到UserOpCompatibleInterface使用第7節(jié)Interface ODS規(guī)范中的StaticInterfaceMethod和InterfaceMethod為這個(gè)Interface指定了獲取Operation輸入操作數(shù)名字,輸出操作數(shù)名字,操作數(shù)以及長(zhǎng)度,結(jié)果以及長(zhǎng)度等方法。然后在OneFlow的oneflow/ir/include/OneFlow/OneFlowUserOps.td中使用DeclareOpInterfaceMethods來(lái)為各種Operation指定Interface,在生成的Operation代碼中就會(huì)帶上這個(gè)Interface聲明。
那么這樣做有什么好處嗎?第一點(diǎn)就是由于OneFlow的UserOp都帶上了UserOpCompatibleInterface,只要我們?yōu)镺neFlow的UserOp實(shí)現(xiàn)一個(gè)通用的GetInputKeys函數(shù),那么所有UserOp派生出來(lái)的Operation都擁有了這個(gè)函數(shù)的功能,因?yàn)樗鼈兌紟狭薝serOpCompatibleInterface這個(gè)接口。
更加通用的例子是基于InterFace來(lái)開(kāi)發(fā)一些通用Pass,比如內(nèi)聯(lián)和形狀推導(dǎo)Pass。見(jiàn)【從零開(kāi)始學(xué)深度學(xué)習(xí)編譯器】十三,如何在MLIR里面寫(xiě)Pass?
0x10. 總結(jié)
這篇文章主要介紹了一下MLIR的Interface,在MLIR文檔的基礎(chǔ)上添加了一些自己的理解和描述,以及展示了OneFlow的一個(gè)例子,以此來(lái)說(shuō)明Interface的好處以及如何使用ODS來(lái)寫(xiě)Interface。
