算子规范化
算子規(guī)范化
規(guī)范化是編譯器IR設(shè)計(jì)的重要組成部分:它使實(shí)現(xiàn)可靠的編譯器轉(zhuǎn)換和確定代碼中優(yōu)劣的原因變得更加容易,并且使有關(guān)IR特定級(jí)別的目標(biāo)的討論變得更加有趣。丹·高曼(Dan Gohman)寫(xiě)了一篇文章 探討這些問(wèn)題。如果不熟悉這些概念,則值得閱讀。
大多數(shù)編譯器都有規(guī)范化的遍歷,有時(shí)它們有很多不同的遍歷(例如LLVM中的instcombine,dag Combine等)。因?yàn)镸LIR是一個(gè)多級(jí)IR,所以可以提供一個(gè)規(guī)范的基礎(chǔ)架構(gòu),并在它代表的許多不同IR中重用它。本文介紹了通用方法,執(zhí)行全局規(guī)范化,并提供了一些內(nèi)容,以捕獲特定于IR的規(guī)則以供參考。
總體設(shè)計(jì)
MLIR具有一次規(guī)范化遍歷,它以貪婪的方式迭代地應(yīng)用規(guī)范化轉(zhuǎn)換,直到IR收斂為止。這些轉(zhuǎn)換由算子本身定義,允許每個(gè)語(yǔ)言一起定義自己的一組算子和規(guī)范化。
關(guān)于wrt規(guī)范化模式的一些重要事項(xiàng):
? 模式的重復(fù)應(yīng)用應(yīng)收斂。不穩(wěn)定或周期性的重寫(xiě)將導(dǎo)致規(guī)范化器中的無(wú)限循環(huán)。
? 通常對(duì)重復(fù)算子時(shí)規(guī)范化,最好使用較少值的算子,因?yàn)槟承┠J絻H在值具有單個(gè)用戶時(shí)才匹配。例如,通常最好將“ x + x”規(guī)范化為“ x * 2”,因?yàn)檫@樣可以將x的使用次數(shù)減少一半。
? 在可能的情況下,最好完全消除算子,例如折疊已知身份(例如“ x + 0 = x”),這總是好事。
在全局范圍內(nèi)適用的規(guī)則
這些轉(zhuǎn)換適用于所有級(jí)別的IR:
? 消除無(wú)副作用,無(wú)用的算子。
? 不斷折疊-例如,“(addi 1,2)”到“ 3”。固定折疊鉤由算子指定。
? 將常量算子移動(dòng)到可交換運(yùn)算符的右側(cè)-例如,將“(addi 4,x)”移動(dòng)到“(addi x,4)”。
? constant-like算子是唯一的,并被提升到第一父屏障區(qū)域的入口塊中。這是一個(gè)與上方隔離的區(qū)域,例如功能的輸入框,或者是通過(guò)shouldMaterializeInto方法標(biāo)記為障礙的區(qū)域DialectFoldInterface。
定義Canonicalizations
有兩種定義規(guī)范的機(jī)制。 getCanonicalizationPatterns和fold。
用getCanonicalizationPatterns規(guī)范化
這種機(jī)制允許以RewritePatterns的形式提供規(guī)范化,這些規(guī)范化是 在C ++中強(qiáng)制性定義的,或者是聲明性地稱(chēng)為 “聲明性重寫(xiě)規(guī)則” 。模式重寫(xiě)基礎(chǔ)結(jié)構(gòu)允許表達(dá)許多不同類(lèi)型的規(guī)范化。這些轉(zhuǎn)換可能很簡(jiǎn)單,例如用移位替換乘法,甚至用無(wú)條件分支替換條件分支。
在 ODS中 ,算子可以設(shè)置該hasCanonicalizer位,生成getCanonicalizationPatterns方法的聲明。
def MyOp : … {
let hasCanonicalizer = 1;
}
然后可以在源文件中提供規(guī)范化模式:
void MyOp::getCanonicalizationPatterns(OwningRewritePatternList &patterns,
MLIRContext *context) {
patterns.insert<…>(…);
}
有關(guān) 定義算子重寫(xiě)的信息,請(qǐng)參見(jiàn) 快速入門(mén)指南。
用規(guī)范化fold
該fold機(jī)制是有意限制的,但是功能強(qiáng)大的機(jī)制允許在整個(gè)編譯器的許多位置應(yīng)用規(guī)范化。例如,canonicalizer pass外部,fold在所述內(nèi)使用 語(yǔ)言轉(zhuǎn)換基礎(chǔ)結(jié)構(gòu) 作為合法化機(jī)制,并且可以經(jīng)由 OpBuilder::createOrFold用在任何地方直接調(diào)用OpBuilder。
fold具有不能創(chuàng)建任何新算子的限制,并且只能替換根算子。它允許就地更新算子,或返回一組預(yù)先存在的值(或?qū)傩?#xff09;來(lái)替換算子。這樣可以確保該fold方法是真正的“本地”轉(zhuǎn)換,并且可以在不需要模式重寫(xiě)器的情況下調(diào)用該方法。
在 ODS中 ,算子可以設(shè)置該hasFolder位以生成fold方法的聲明。此方法采用不同的形式,具體取決于算子的結(jié)構(gòu)。
def MyOp : … {
let hasFolder = 1;
}
如果算子只有一個(gè)結(jié)果,則將生成以下內(nèi)容:
/// Implementations of this hook can only perform the following changes to the
/// operation:
///
/// 1. They can leave the operation alone and without changing the IR, and
/// return nullptr.
/// 2. They can mutate the operation in place, without changing anything else
/// in the IR. In this case, return the operation itself.
/// 3. They can return an existing value or attribute that can be used instead
/// of the operation. The caller will remove the operation and use that
/// result instead.
///
OpFoldResult MyOp::fold(ArrayRef operands) {
…
}
否則,將生成以下內(nèi)容:
/// Implementations of this hook can only perform the following changes to the
/// operation:
///
/// 1. They can leave the operation alone and without changing the IR, and
/// return failure.
/// 2. They can mutate the operation in place, without changing anything else
/// in the IR. In this case, return success.
/// 3. They can return a list of existing values or attribute that can be used
/// instead of the operation. In this case, fill in the results list and
/// return success. The results list must correspond 1-1 with the results of
/// the operation, partial folding is not supported. The caller will remove
/// the operation and use those results instead.
///
LogicalResult MyOp::fold(ArrayRef operands,
SmallVectorImpl &results) {
…
}
在上面,為每種方法ArrayRef提供了一個(gè)與每個(gè)算子的常量屬性值相對(duì)應(yīng)的。這些算子是實(shí)現(xiàn)ConstantLike特征的算子。如果任何一個(gè)算子不是常數(shù),則將Attribute提供一個(gè)空值。例如,如果提供MYOP三個(gè)算子[ a,b,c],但只b是常量,那么operands將是以下形式的[屬性(),b值,屬性()]。
同樣在上面,使用OpFoldResult。此類(lèi)表示折疊算子結(jié)果的可能結(jié)果:SSAValue或 Attribute(對(duì)于恒定結(jié)果)。如果Value提供了SSA ,則它必須 對(duì)應(yīng)于現(xiàn)有值。該fold方法不允許生成new Value。Attribute返回值的形式?jīng)]有具體限制 ,但重要的是要確保Attribute 具體表示形式的Type一致性。
當(dāng)fold算子上的鉤子算子不成功時(shí),語(yǔ)言可以通過(guò)實(shí)現(xiàn)DialectFoldInterface和覆蓋折疊鉤子來(lái)提供后備功能。
從屬性生成常數(shù)
當(dāng)一個(gè)fold方法返回aAttribute作為結(jié)果時(shí),表示該結(jié)果是“常量”。Attribute是該值的常數(shù)表示。該fold方法的用戶(例如規(guī)范化過(guò)程)將采用這些Attributes,并在IR中實(shí)現(xiàn)常量算子來(lái)表示。為了實(shí)現(xiàn)這種實(shí)現(xiàn),算子的語(yǔ)言必須實(shí)現(xiàn)materializeConstant鉤子。該鉤子接受一個(gè)Attribute 通常由返回的值,fold并產(chǎn)生實(shí)現(xiàn)該值的“類(lèi)似常數(shù)”的算子。
在 ODS中 ,語(yǔ)言可以將hasConstantMaterializer位設(shè)置為生成materializeConstant方法的聲明。
def MyDialect_Dialect : … {
let hasConstantMaterializer = 1;
}
然后可以在源文件中實(shí)現(xiàn)常量:
/// Hook to materialize a single constant operation from a given attribute value
/// with the desired resultant type. This method should use the provided builder
/// to create the operation without changing the insertion position. The
/// generated operation is expected to be constant-like. On success, this hook
/// should return the value generated to represent the constant value.
/// Otherwise, it should return nullptr on failure.
Operation *MyDialect::materializeConstant(OpBuilder &builder, Attribute value,
Type type, Location loc) {
…
}
總結(jié)
- 上一篇: Pass Infrastructure基
- 下一篇: Pass Infrastructure基