<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【從零開始學深度學習編譯器】十七,MLIR ODS要點總結(jié)下篇

          共 12415字,需瀏覽 25分鐘

           ·

          2021-12-15 08:45

          前言

          這一節(jié)在【從零開始學深度學習編譯器】十六,MLIR ODS要點總結(jié)上篇 的基礎(chǔ)上補充完整了ODS的要點。約束和屬性的定義都是MLIR中相當重要的元素,至于類型的定義個人認為了解即可,等到我們需要自定義類型的時候再仔細研究。最后MLIR的語法比較晦澀,初學者可以借助mlir-tblgen來輔助debug。

          在這兩篇文章里,我跟著MLIR的ODS規(guī)范完整走了一遍并總結(jié)了14個要點,對于每一個要點我都在OneFlow MLIR的Op定義中進行了對照,并給出了一些示例代碼和位置。希望對讀者入門MLIR有幫助。上篇和下篇對應(yīng)的markdown文件我放在:https://github.com/BBuf/tvm_mlir_learn 倉庫了,有需要的自取。

          11. 約束(這個很重要)

          約束(Constraint)是表驅(qū)動Operation定義中的一個核心概念:Operation驗證和圖Operation匹配都是基于約束來做的。因此,Operation定義和重寫規(guī)則都直接涉及寫入約束。MLIR在OpBase.td(https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/OpBase.td)中定義了Constraint基類。一個Operation的約束可以覆蓋不同的范圍,可能是:

          • 僅關(guān)注單個屬性(例如大于 5 的 32 位整數(shù))
          • 多個操作數(shù)和結(jié)果(例如,第一個結(jié)果的形狀必須與第一個操作數(shù)(可理解為Tensor)相同)
          • 操作本身固有的。(例如沒有副作用,參考Transpose Op消除那個案例)

          我們將它們分別稱為單實體約束、多實體約束和特征。這里的概念了解下即可,我覺得寫新的約束是最重要的。

          • 單體約束。單體約束作用域為單個操作數(shù),屬性或結(jié)果的約束在實體的聲明位置進行指定,如Operation argumentsOperation results 中(在【從零開始學深度學習編譯器】十六,MLIR ODS要點總結(jié)上篇 中總結(jié)了Operation arguments和Operation results需要注意的知識)。

          • 多實體約束。多實體約束在https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/OpBase.td中被建模為PredOpTrait類(是OpTrait的一個子類)。查看OpBase.td獲取完整列表。

          • 特征。特征是Operation的內(nèi)在屬性,例如是否具有副作用、可交換與否、是否是終止符等。這些約束應(yīng)指定為 Op 類模板參數(shù),如【從零開始學深度學習編譯器】十六,MLIR ODS要點總結(jié)上篇 中第三節(jié)的Op的特征和約束(Operation traits and constraints) 所示。特征在https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/OpBase.td中被建模成一個NativeOpTrait類(OpTrait的一個子類)。它們得到支持并將被翻譯成相應(yīng)的 C++ mlir::OpTrait 類。

          • 如何指定新的約束?要寫一個新的約束,我們必須為它提供一個謂詞并指定一個描述名。使用Pred類建模的謂詞是構(gòu)成約束的核心。約束的謂詞通常以嵌套的方式構(gòu)建,有兩種類型的謂詞:1.CPred:原始的葉子節(jié)點謂詞。2.復合謂詞:由使用謂詞組合器的子謂詞組成的謂詞(conjunction: And, disjunction: Or, negation: Neg, substitution: SubstLeaves, concatenation: Concat)。CPred 是構(gòu)成更復雜謂詞的基礎(chǔ)。它是TableGen 視角下的“原子”謂詞,是TableGen 與C++ 之間的“接口”。里面已經(jīng)是 C++ 代碼了,它會被當作不透明的字符串來處理,并帶有特殊的占位符來替換。我們可以將任何返回布爾值的 C++ 代碼放在 CPred 中,包括計算表達式、調(diào)用函數(shù)、調(diào)用類方法等。

          為了幫助與 C++ 環(huán)境交互,提供了一些特殊的占位符來引用使用該謂詞的上下文中的實體。它們充當封閉環(huán)境的“鉤子”。這包括 $_builder、$_op$_self

          • $_builder會被替換成一個mlir::Builder實例,以便我們可以訪問常見的構(gòu)建方法。
          • $_op 會被當前的Operation替換,以便我們可以訪問當前Operation的信息。
          • $_self 會被替換為該謂詞所附加的實體。例如,BoolAttr 是一個包含 CPred<"$_self.isa()"> 的屬性約束。那么對于 BoolAttr:$attr$_self 將被 $attr 替換。對于類型約束,它有點特殊,因為我們希望每個類型定義的約束自然讀取,并且我們希望將類型約束直接附加到操作數(shù)/結(jié)果,$_self 將被操作數(shù)/結(jié)果的類型替換。例如,對于 F32:$operand 中的 F32,它的 $_self 將被擴展為operand(...).getType()。

          例如,要寫一個屬性 attr 是一個 IntegerAttr,在 C++ 中我們可以調(diào)用 attr.isa()來實現(xiàn)。這行代碼也可以作為 $_self.isa() 包裝在 CPred 中,其中 $_self 作為特殊占位符,在擴展時由當前屬性 attr 替換來實現(xiàn)相同的功能(指在Tablegen中)。

          對于更復雜的謂詞,我們可以將其包裝在單個 CPred 中,也可以使用謂詞組合器將它們組合起來。例如,要寫出屬性 attr 是 32 位或 64 位整數(shù)的約束,可以將其寫為:

          And<[
          ??CPred<"$_self.isa()">,
          ??Or<[
          ????CPred<"$_self.cast().getType().isInteger(32)">,
          ????CPred<"$_self.cast().getType().isInteger(64)">
          ??]>
          ]>

          (注意,上面只是用一個熟悉的例子來展示如何使用CPred和謂詞組合器來編寫復雜的謂詞。具體來說,對于整數(shù)屬性,OpBase.td已經(jīng)定義了I32AttrI64Attr。所以我們實際上可以重用它們來編寫它 Or<[I32Attr.predicate, I64Attr.predicate]>.)

          這里再以O(shè)neFlow的一個例子來講解一下,我們定義了一個IsGPU的約束:

          def?IsGPU:?Constraint"$0.getValue().equals(\"gpu\")">,?"is?GPU?device">;

          然后OneFlow在Transformer部分做了一個定制優(yōu)化,就是將Scale和Tril這兩個連續(xù)的Kernel融合成一個大的Kernel,這樣可以省掉一部分內(nèi)存讀寫的時間。但這個融合的kernel只在GPU的情況下生效,所以這個時候就需要判斷當前計算圖檢測到的Scale和Tril這兩個Operation的device是否是GPU的,就需要這個約束。FusedScaleTrilPattern這個Pass的實現(xiàn)如下,可以看到在最后使用了IsGPU這個約束。

          def?FusedScaleTrilPattern?:?Pat<
          ??(
          ????OneFlow_TrilOp
          ????(
          ??????OneFlow_ScalarMulOp
          ????????$x,
          ????????$scale_op_name,
          ????????$scale_trainable,
          ????????$scale_device_tag,
          ????????$scale_device_name,
          ????????$scale_scope_symbol_id,
          ????????$scale_hierarchy,
          ????????$has_int_operand,
          ????????$has_float_operand,
          ????????$int_operand,
          ????????$float_operand
          ????),
          ????$tril_op_name,
          ????$tril_trainable,
          ????$tril_device_tag,
          ????$tril_device_name,
          ????$tril_scope_symbol_id,
          ????$tril_hierarchy,
          ????$diagonal,
          ????$floating_fill_value,
          ????$integer_fill_value,
          ????$is_floating_fill_value
          ??),
          ??(OneFlow_FusedScaleTrilOp?$x,
          ????$tril_op_name,
          ????$tril_trainable,
          ????$tril_device_tag,
          ????$tril_device_name,
          ????$tril_scope_symbol_id,
          ????$tril_hierarchy,
          ????$diagonal,
          ????$floating_fill_value,
          ????$integer_fill_value,
          ????$is_floating_fill_value,
          ????$float_operand,
          ????$int_operand,
          ????$has_float_operand
          ??),
          ??[
          ????(IsGPU?$tril_device_tag),
          ????(IsGPU?$scale_device_tag)
          ??]
          >;

          這個Pass的功能就是檢測到連續(xù)的Scale+Tril Operation就將這兩個Operation融合成一個FusedScaleTril Operation。

          如果謂詞用 CPred 和謂詞組合器一起編寫非常復雜,我們也可以將其編寫為普通的 C++ 函數(shù),并使用 CPred 作為“調(diào)用”函數(shù)的一種方式。例如,要驗證屬性 attr 是否具有某些屬性,我們可以編寫一個 C++ 函數(shù),如:

          bool?HasSomeProperty(Attribute?attr)?{?...?}

          然后定義Op如下:

          def?HasSomeProperty?:?AttrConstraint"HasSomeProperty($_self)">,
          ?????????????????????????????????????"has?some?property">;

          def?MyOp?:?Op<...>?{
          ??let?arguments?=?(ins
          ????...
          ????HasSomeProperty:$attr
          ??);
          }

          至于我們是否應(yīng)該使用單個 CPred 包裝整個表達式、多個帶有謂詞組合器的 CPreds 或單個 CPred “調(diào)用”一個函數(shù)來定義謂詞,沒有明確的標準。使用 CPred 和謂詞組合器進行定義是可取的,因為它將更多信息(而不是隱藏 C++ 函數(shù)背后的所有邏輯)公開到操作定義規(guī)范中,以便它可以潛在地驅(qū)動更多的自動生成案例。但它需要一個很好的通用謂詞庫作為構(gòu)建塊,以避免重復,目前正在研究中。

          12. 屬性定義(很重要+1)

          屬性是編譯期就知道的Operation的常量。ODS 在 C++ 屬性類上提供屬性包裝器。MLIR 的核心 IR 庫中定義了一些常見的 C++ 屬性類(https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/Attributes.h)。ODS 允許在 TableGen 中使用這些屬性來定義Operation,可能具有更細粒度的約束。比如StrAttr直接映射到StringAttrF32Attr/F64Attr 要求 FloatAttr 額外具有一定的位寬。ODS屬性被定義為具有存儲類型(對應(yīng)于存儲屬性的mlir::Attribute類),返回類型(對應(yīng)于生成的getters幫助函數(shù)的C++返回類型)以及在內(nèi)部存儲類型和幫助函數(shù)進行互轉(zhuǎn)的方法。

          屬性裝飾器。有一些重要的屬性適配器/裝飾器/修飾符可以應(yīng)用于 ODS 屬性以指定常見的附加屬性,如可選性、默認值等。

          • DefaultValuedAttr:為一個屬性指定默認值。
          • OptionalAttr:將一個屬性指定為可選的。
          • ConfinedConfined作為一種通用機制被提供,以幫助對值類型帶來的屬性約束進行進一步建模??梢酝ㄟ^Confined將較為原始的約束組合成為復雜約束。舉個例子,一個32bit的整型最小值為10,可以被表示為Confined]>。還有一些其它例子,比如IntMinValue:指定一個大于等于N的整型屬性等等。

          枚舉屬性 。某些屬性只能從預(yù)定義的enum獲取值,例如,比較op的比較類型。為了定義這些屬性,ODS 提供了幾種機制:StrEnumAttr、IntEnumAttrBitEnumAttr

          • StrEnumAttr:每個enum case 都是一個字符串,屬性在op中存儲為 StringAttr。
          • IntEnumAttr:每個enum case 都是一個整數(shù),屬性在op中存儲為 IntegerType。
          • BitEnumAttr:每個 enum case 都是一個位,屬性在 op 中存儲為 IntegerAttr。

          所有這些 *EnumAttr 屬性都需要通過其對應(yīng)的 *EnumAttrCase 完全指定所有允許的情況。有了這個,ODS 能夠生成額外的驗證以只接受允許的案例。 為了促進 *EnumAttrs 和它們的 C++ 使用者之間的交互,EnumsGen(https://github.com/llvm/llvm-project/blob/main/mlir/tools/mlir-tblgen/EnumsGen.cpp) TableGen 后端可以生成一些常見的實用程序:C++ 枚舉類、用于枚舉類的 llvm::DenseMapInfo、從/到字符串的轉(zhuǎn)換函數(shù)。這是通過 mlir-tblgen-gen-enum-decls-gen-enum-defs 命令行選項控制的。

          例如,給定下面的EnumAttr

          def?Case15:?I32EnumAttrCase<"Case15",?15>;
          def?Case20:?I32EnumAttrCase<"Case20",?20>;

          def?MyIntEnum:?I32EnumAttr<"MyIntEnum",?"An?example?int?enum",
          ???????????????????????????[Case15,?Case20]>?{
          ??let?cppNamespace?=?"Outer::Inner";
          ??let?stringToSymbolFnName?=?"ConvertToEnum";
          ??let?symbolToStringFnName?=?"ConvertToString";
          }

          以下代碼將通過 mlir-tblgen -gen-enum-decls 生成:

          namespace?Outer?{
          namespace?Inner?{
          //?An?example?int?enum
          enum?class?MyIntEnum?:?uint32_t?{
          ??Case15?=?15,
          ??Case20?=?20,
          };

          llvm::Optional?symbolizeMyIntEnum(uint32_t);
          llvm::StringRef?ConvertToString(MyIntEnum);
          llvm::Optional?ConvertToEnum(llvm::StringRef);
          inline?constexpr?unsigned?getMaxEnumValForMyIntEnum()?{
          ??return?20;
          }

          }?//?namespace?Inner
          }?//?namespace?Outer

          namespace?llvm?{
          template<>?struct?DenseMapInfo?{
          ??using?StorageInfo?=?llvm::DenseMapInfo<uint32_t>;

          ??static?inline?Outer::Inner::MyIntEnum?getEmptyKey()?{
          ????return?static_cast(StorageInfo::getEmptyKey());
          ??}

          ??static?inline?Outer::Inner::MyIntEnum?getTombstoneKey()?{
          ????return?static_cast(StorageInfo::getTombstoneKey());
          ??}

          ??static?unsigned?getHashValue(const?Outer::Inner::MyIntEnum?&val)?{
          ????return?StorageInfo::getHashValue(static_cast<uint32_t>(val));
          ??}

          ??static?bool?isEqual(const?Outer::Inner::MyIntEnum?&lhs,?const?Outer::Inner::MyIntEnum?&rhs)?{
          ????return?lhs?==?rhs;
          ??}
          };
          }

          以下代碼將通過 mlir-tblgen -gen-enum-defs 生成:

          namespace?Outer?{
          namespace?Inner?{
          llvm::StringRef?ConvertToString(MyIntEnum?val)?{
          ??switch?(val)?{
          ????case?MyIntEnum::Case15:?return?"Case15";
          ????case?MyIntEnum::Case20:?return?"Case20";
          ??}
          ??return?"";
          }

          llvm::Optional?ConvertToEnum(llvm::StringRef?str)?{
          ??return?llvm::StringSwitch>(str)
          ??????.Case("Case15",?MyIntEnum::Case15)
          ??????.Case("Case20",?MyIntEnum::Case20)
          ??????.Default(llvm::None);
          }
          llvm::Optional?symbolizeMyIntEnum(uint32_t?value)?{
          ??switch?(value)?{
          ??case?15:?return?MyIntEnum::Case15;
          ??case?20:?return?MyIntEnum::Case20;
          ??default:?return?llvm::None;
          ??}
          }

          }?//?namespace?Inner
          }?//?namespace?Outer

          對于以下 BitEnumAttr 定義類似:

          def?None:?BitEnumAttrCase<"None",?0x0000>;
          def?Bit1:?BitEnumAttrCase<"Bit1",?0x0001>;
          def?Bit2:?BitEnumAttrCase<"Bit2",?0x0002>;
          def?Bit3:?BitEnumAttrCase<"Bit3",?0x0004>;

          def?MyBitEnum:?BitEnumAttr<"MyBitEnum",?"An?example?bit?enum",
          ???????????????????????????[None,?Bit1,?Bit2,?Bit3]>;

          我們得到:

          //?An?example?bit?enum
          enum?class?MyBitEnum?:?uint32_t?{
          ??None?=?0,
          ??Bit1?=?1,
          ??Bit2?=?2,
          ??Bit3?=?4,
          };

          llvm::Optional?symbolizeMyBitEnum(uint32_t);
          std::string?stringifyMyBitEnum(MyBitEnum);
          llvm::Optional?symbolizeMyBitEnum(llvm::StringRef);
          inline?MyBitEnum?operator|(MyBitEnum?lhs,?MyBitEnum?rhs)?{
          ??return?static_cast(static_cast<uint32_t>(lhs)?|?static_cast<uint32_t>(rhs));
          }
          inline?MyBitEnum?operator&(MyBitEnum?lhs,?MyBitEnum?rhs)?{
          ??return?static_cast(static_cast<uint32_t>(lhs)?&?static_cast<uint32_t>(rhs));
          }
          inline?bool?bitEnumContains(MyBitEnum?bits,?MyBitEnum?bit)?{
          ??return?(static_cast<uint32_t>(bits)?&?static_cast<uint32_t>(bit))?!=?0;
          }

          namespace?llvm?{
          template<>?struct?DenseMapInfo<::MyBitEnum>?{
          ??using?StorageInfo?=?llvm::DenseMapInfo<uint32_t>;

          ??static?inline?::MyBitEnum?getEmptyKey()?{
          ????return?static_cast<::MyBitEnum>(StorageInfo::getEmptyKey());
          ??}

          ??static?inline?::MyBitEnum?getTombstoneKey()?{
          ????return?static_cast<::MyBitEnum>(StorageInfo::getTombstoneKey());
          ??}

          ??static?unsigned?getHashValue(const?::MyBitEnum?&val)?{
          ????return?StorageInfo::getHashValue(static_cast<uint32_t>(val));
          ??}

          ??static?bool?isEqual(const?::MyBitEnum?&lhs,?const?::MyBitEnum?&rhs)?{
          ????return?lhs?==?rhs;
          ??}
          };
          std::string?stringifyMyBitEnum(MyBitEnum?symbol)?{
          ??auto?val?=?static_cast<uint32_t>(symbol);
          ??//?Special?case?for?all?bits?unset.
          ??if?(val?==?0)?return?"None";

          ??llvm::SmallVector2>?strs;
          ??if?(1u?&?val)?{?strs.push_back("Bit1");?val?&=?~1u;?}
          ??if?(2u?&?val)?{?strs.push_back("Bit2");?val?&=?~2u;?}
          ??if?(4u?&?val)?{?strs.push_back("Bit3");?val?&=?~4u;?}

          ??if?(val)?return?"";
          ??return?llvm::join(strs,?"|");
          }

          llvm::Optional?symbolizeMyBitEnum(llvm::StringRef?str)?{
          ??//?Special?case?for?all?bits?unset.
          ??if?(str?==?"None")?return?MyBitEnum::None;

          ??llvm::SmallVector2>?symbols;
          ??str.split(symbols,?"|");

          ??uint32_t?val?=?0;
          ??for?(auto?symbol?:?symbols)?{
          ????auto?bit?=?llvm::StringSwitchuint32_t>>(symbol)
          ??????.Case("Bit1",?1)
          ??????.Case("Bit2",?2)
          ??????.Case("Bit3",?4)
          ??????.Default(llvm::None);
          ????if?(bit)?{?val?|=?*bit;?}?else?{?return?llvm::None;?}
          ??}
          ??return?static_cast(val);
          }

          llvm::Optional?symbolizeMyBitEnum(uint32_t?value)?{
          ??//?Special?case?for?all?bits?unset.
          ??if?(value?==?0)?return?MyBitEnum::None;

          ??if?(value?&?~(1u?|?2u?|?4u))?return?llvm::None;
          ??return?static_cast(value);
          }

          在OneFlow-MLIR中同樣也有枚舉屬性的定義用來處理OneFlow的各種數(shù)據(jù)類型,代碼如下:

          #ifndef?ONEFLOW_ENUMS
          #define?ONEFLOW_ENUMS

          def?OneFlow_InvalidDataType?:?I32EnumAttrCase<"DT_InvalidDataType",?0>;
          def?OneFlow_Char?:?I32EnumAttrCase<"DT_Char",?1>;
          def?OneFlow_Float?:?I32EnumAttrCase<"DT_Float",?2>;
          def?OneFlow_Double?:?I32EnumAttrCase<"DT_Double",?3>;
          def?OneFlow_Int8?:?I32EnumAttrCase<"DT_Int8",?4>;
          def?OneFlow_Int32?:?I32EnumAttrCase<"DT_Int32",?5>;
          def?OneFlow_Int64?:?I32EnumAttrCase<"DT_Int64",?6>;
          def?OneFlow_UInt8?:?I32EnumAttrCase<"DT_UInt8",?7>;
          def?OneFlow_OFRecord?:?I32EnumAttrCase<"DT_OFRecord",?8>;
          def?OneFlow_Float16?:?I32EnumAttrCase<"DT_Float16",?9>;
          def?OneFlow_TensorBuffer:?I32EnumAttrCase<"DT_TensorBuffer",?10>;

          def?OneFlow_DataType:?I32EnumAttr<"DataType",?"OneFlow?Data?Type?enum",
          ??[
          ????OneFlow_InvalidDataType,
          ????OneFlow_Char,
          ????OneFlow_Float,
          ????OneFlow_Double,
          ????OneFlow_Int8,
          ????OneFlow_Int32,
          ????OneFlow_Int64,
          ????OneFlow_UInt8,
          ????OneFlow_OFRecord,
          ????OneFlow_Float16,
          ????OneFlow_TensorBuffer,
          ??]
          >?{
          ??let?cppNamespace?=?"::mlir::oneflow";
          ??let?stringToSymbolFnName?=?"ConvertToEnum";
          ??let?symbolToStringFnName?=?"ConvertToString";
          }

          #endif?//?ONEFLOW_ENUMS

          我們可以觀察一下它生成的enum屬性聲明:

          /*===-?TableGen'erated?file?-------------------------------------*-?C++?-*-===*\
          |*????????????????????????????????????????????????????????????????????????????*|
          |*?Enum?Utility?Declarations??????????????????????????????????????????????????*|
          |*????????????????????????????????????????????????????????????????????????????*|
          |*?Automatically?generated?file,?do?not?edit!?????????????????????????????????*|
          |*????????????????????????????????????????????????????????????????????????????*|
          \*===----------------------------------------------------------------------===*/


          namespace?mlir?{
          namespace?oneflow?{
          //?OneFlow?Data?Type?enum
          enum?class?DataType?:?uint32_t?{
          ??DT_InvalidDataType?=?0,
          ??DT_Char?=?1,
          ??DT_Float?=?2,
          ??DT_Double?=?3,
          ??DT_Int8?=?4,
          ??DT_Int32?=?5,
          ??DT_Int64?=?6,
          ??DT_UInt8?=?7,
          ??DT_OFRecord?=?8,
          ??DT_Float16?=?9,
          ??DT_TensorBuffer?=?10,
          };

          ::llvm::Optional?symbolizeDataType(uint32_t);
          ::llvm::StringRef?ConvertToString(DataType);
          ::llvm::Optional?ConvertToEnum(::llvm::StringRef);
          inline?constexpr?unsigned?getMaxEnumValForDataType()?{
          ??return?10;
          }


          inline?::llvm::StringRef?stringifyEnum(DataType?enumValue)?{
          ??return?ConvertToString(enumValue);
          }

          template?<typename?EnumType>
          ::llvm::Optional?symbolizeEnum(::llvm::StringRef);

          template?<>
          inline?::llvm::Optional?symbolizeEnum(::llvm::StringRef?str)?{
          ??return?ConvertToEnum(str);
          }

          class?DataTypeAttr?:?public?::mlir::IntegerAttr?{
          public:
          ??using?ValueType?=?DataType;
          ??using?::mlir::IntegerAttr::IntegerAttr;
          ??static?bool?classof(::mlir::Attribute?attr);
          ??static?DataTypeAttr?get(::mlir::MLIRContext?*context,?DataType?val);
          ??DataType?getValue()?const;
          };
          }?//?namespace?oneflow
          }?//?namespace?mlir

          namespace?llvm?{
          template<>?struct?DenseMapInfo<::mlir::oneflow::DataType>?{
          ??using?StorageInfo?=?::llvm::DenseMapInfo<uint32_t>;

          ??static?inline?::mlir::oneflow::DataType?getEmptyKey()?{
          ????return?static_cast<::mlir::oneflow::DataType>(StorageInfo::getEmptyKey());
          ??}

          ??static?inline?::mlir::oneflow::DataType?getTombstoneKey()?{
          ????return?static_cast<::mlir::oneflow::DataType>(StorageInfo::getTombstoneKey());
          ??}

          ??static?unsigned?getHashValue(const?::mlir::oneflow::DataType?&val)?{
          ????return?StorageInfo::getHashValue(static_cast<uint32_t>(val));
          ??}

          ??static?bool?isEqual(const?::mlir::oneflow::DataType?&lhs,?const?::mlir::oneflow::DataType?&rhs)?{
          ????return?lhs?==?rhs;
          ??}
          };
          }

          實現(xiàn)部分就不貼了,這里貼了過長的代碼了。

          13. 類型定義(我只是簡單了解了一下)

          MLIR 定義了 TypeDef 類層次結(jié)構(gòu),以支持根據(jù)其規(guī)范生成數(shù)據(jù)類型。類型是通過特化 TypeDef 類來定義的,該類具有它所需的所有字段的具體內(nèi)容。例如,整數(shù)類型可以定義為:

          //?All?of?the?types?will?extend?this?class.
          class?Test_Type?:?TypeDef?{?}

          //?An?alternate?int?type.
          def?IntegerType?:?Test_Type<"TestInteger">?{
          ??let?mnemonic?=?"int";

          ??let?summary?=?"An?integer?type?with?special?semantics";

          ??let?description?=?[{
          ????An?alternate?integer?type.?This?type?differentiates?itself?from?the
          ????standard?integer?type?by?not?having?a?SignednessSemantics?parameter,?just
          ????a?width.
          ??}];

          ??let?parameters?=?(ins?"unsigned":$width);

          ??//?We?define?the?printer?inline.
          ??let?printer?=?[{
          ????$_printer?<"int<"?<width?<">";
          ??}];

          ??//?The?parser?is?defined?here?also.
          ??let?parser?=?[{
          ????if?($_parser.parseLess())
          ??????return?Type();
          ????int?width;
          ????if?($_parser.parseInteger(width))
          ??????return?Type();
          ????if?($_parser.parseGreater())
          ??????return?Type();
          ????return?get($_ctxt,?width);
          ??}];
          }
          • Type name : 生成的 C++ 類的名稱默認為 Type(例如上例中的 TestIntegerType)。這可以通過 cppClassName 字段覆蓋。mnemonic 是指定解析的asm名稱。它是可選的,不指定將意味著沒有解析器或打印方法附加到此類。

          • Type documentation:存在summarydescription字段,其使用方式與Operation中相同。即,summary應(yīng)該是單行的,而description應(yīng)該是更長的解釋。

          • Type parametersparameters字段是類型參數(shù)的列表。如果未指定任何參數(shù)(默認),則此類型被視為單例類型。參數(shù)采用“c++Type”:$paramName 格式。要將C++類型用作需要在存儲構(gòu)造函數(shù)中分配的參數(shù),有兩種選擇:1. 設(shè)置 hasCustomStorageConstructor 以生成帶有剛剛聲明的構(gòu)造函數(shù)的 TypeStorage 類——沒有定義——所以我們可以自己編寫它。2. 使用TypeParameter tablegen類而不是"c++Type"字符串。(后半句話我不是很懂,也還沒用過。)

          • TypeParameter tablegen class : 這用于進一步指定有關(guān)每個類型參數(shù)的屬性。它包括文檔(summarysyntax)、要使用的 C++ 類型、要在存儲構(gòu)造函數(shù)方法中使用的自定義分配器,以及用于確定參數(shù)類型的兩個實例是否相等的自定義比較器。

          //?DO?NOT?DO?THIS!
          let?parameters?=?(ins?"ArrayRef":$dims);

          默認存儲構(gòu)造函數(shù)盲目地按值復制字段。它對類型一無所知。在這種情況下,ArrayRef 需要使用 dims = allocator.copyInto(dims) 進行分配。

          class?ArrayRefIntParam?:
          ????TypeParameter<"::llvm::ArrayRef",?"Array?of?ints">?{
          ??let?allocator?=?"$_dst?=?$_allocator.copyInto($_self);";
          }

          ...

          let?parameters?=?(ins?ArrayRefIntParam:$dims);

          allocator代碼塊由$_allocator(是在其中分配對象的 TypeStorageAllocator)和$_dst(是放置已分配數(shù)據(jù)的變量)組成。comparator代碼塊由$_lhs$_rhs參數(shù)類型實例組成。

          自定義Type還有不少內(nèi)容,但目前我沒有這方面的需求,所以就沒有繼續(xù)看了,這里只是簡單了解了一下。感興趣的讀者可以自行查看文檔進行深入研究:https://mlir.llvm.org/docs/OpDefinitions/ 。

          14. DEBUG方法

          使用mlir-tblgen來看產(chǎn)生的文本。TableGen 語法有時可能很晦澀。閱讀生成的文本對于理解和調(diào)試問題非常有用。要構(gòu)建 mlir-tblgen,可以運行 cmake --build 。--target mlir-tblgen 在我們的構(gòu)建目錄中,并在 bin/ 子目錄中找到 mlir-tblgen 二進制文件。所有支持的生成器都可以通過 mlir-tblgen --help 找到。

          要查看生成的代碼,請通過 -I 提供包含路徑,使用 mlir-tblgen 調(diào)用特定生成器。例如:

          #?To?see?op?C++?class?declaration
          mlir-tblgen?--gen-op-decls?-I?/path/to/mlir/include?/path/to/input/td/file
          #?To?see?op?C++?class?definition
          mlir-tblgen?--gen-op-defs?-I?/path/to/mlir/include?/path/to/input/td/file
          #?To?see?op?documentation
          mlir-tblgen?--gen-dialect-doc?-I?/path/to/mlir/include?/path/to/input/td/file

          #?To?see?op?interface?C++?class?declaration
          mlir-tblgen?--gen-op-interface-decls?-I?/path/to/mlir/include?/path/to/input/td/file
          #?To?see?op?interface?C++?class?definition
          mlir-tblgen?--gen-op-interface-defs?-I?/path/to/mlir/include?/path/to/input/td/file
          #?To?see?op?interface?documentation
          mlir-tblgen?--gen-op-interface-doc?-I?/path/to/mlir/include?/path/to/input/td/file

          15. 總結(jié)

          這一節(jié)在【從零開始學深度學習編譯器】十六,MLIR ODS要點總結(jié)上篇 的基礎(chǔ)上補充完整了ODS的要點。約束和屬性的定義都是MLIR中相當重要的元素,至于類型的定義個人認為了解即可,等到我們需要自定義類型的時候再仔細研究。最后MLIR的語法比較晦澀,初學者可以借助mlir-tblgen來輔助debug。

          在這兩篇文章里,我跟著MLIR的ODS規(guī)范完整走了一遍并總結(jié)了14個要點,對于每一個要點我都在OneFlow MLIR的Op定義中進行了對照,并給出了一些示例代碼和位置。希望對讀者入門MLIR有幫助。


          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产熟女乱伦 | 久久亚洲精品成人777 | 久久久91精品国产一区苍井空 | 人人做天天摸夜夜添成人 | 国产综合福利在线 |