• 算子规范化


    算子规范化

    规范化是编译器IR设计的重要组成部分:它使实现可靠的编译器转换和确定代码中优劣的原因变得更加容易,并且使有关IR特定级别的目标的讨论变得更加有趣。丹·高曼(Dan Gohman)写了一篇文章 探讨这些问题。如果不熟悉这些概念,则值得阅读。

    大多数编译器都有规范化的遍历,有时它们有很多不同的遍历(例如LLVM中的instcombine,dag Combine等)。因为MLIR是一个多级IR,所以可以提供一个规范的基础架构,并在它代表的许多不同IR中重用它。本文介绍了通用方法,执行全局规范化,并提供了一些内容,以捕获特定于IR的规则以供参考。

    总体设计 

    MLIR具有一次规范化遍历,它以贪婪的方式迭代地应用规范化转换,直到IR收敛为止。这些转换由算子本身定义,允许每个语言一起定义自己的一组算子和规范化。

    关于wrt规范化模式的一些重要事项:

    • 模式的重复应用应收敛。不稳定或周期性的重写将导致规范化器中的无限循环。
    • 通常对重复算子时规范化,最好使用较少值的算子,因为某些模式仅在值具有单个用户时才匹配。例如,通常最好将“ x + x”规范化为“ x * 2”,因为这样可以将x的使用次数减少一半。
    • 在可能的情况下,最好完全消除算子,例如折叠已知身份(例如“ x + 0 = x”),这总是好事。

    在全局范围内适用的规则 

    这些转换适用于所有级别的IR:

    • 消除无副作用,无用的算子。
    • 不断折叠-例如,“(addi 1,2)”到“ 3”。固定折叠钩由算子指定。
    • 将常量算子移动到可交换运算符的右侧-例如,将“(addi 4,x)”移动到“(addi x,4)”。
    • constant-like算子是唯一的,并被提升到第一父屏障区域的入口块中。这是一个与上方隔离的区域,例如功能的输入框,或者是通过shouldMaterializeInto方法标记为障碍的区域DialectFoldInterface。

    定义Canonicalizations  

    有两种定义规范的机制。 getCanonicalizationPatterns和fold。

    用getCanonicalizationPatterns规范化 

    这种机制允许以RewritePatterns的形式提供规范化,这些规范化是 在C ++中强制性定义的,或者是声明性地称为 “声明性重写规则” 。模式重写基础结构允许表达许多不同类型的规范化。这些转换可能很简单,例如用移位替换乘法,甚至用无条件分支替换条件分支。

    在 ODS中 ,算子可以设置该hasCanonicalizer位,生成getCanonicalizationPatterns方法的声明。

    def MyOp : ... {

      let hasCanonicalizer = 1;

    }

    然后可以在源文件中提供规范化模式:

    void MyOp::getCanonicalizationPatterns(OwningRewritePatternList &patterns,

                                           MLIRContext *context) {

      patterns.insert<...>(...);

    }

    有关 定义算子重写的信息,请参见 快速入门指南

    用规范化fold 

    该fold机制是有意限制的,但是功能强大的机制允许在整个编译器的许多位置应用规范化。例如,canonicalizer pass外部,fold在所述内使用 语言转换基础结构 作为合法化机制,并且可以经由 OpBuilder::createOrFold用在任何地方直接调用OpBuilder。

    fold具有不能创建任何新算子的限制,并且只能替换根算子。它允许就地更新算子,或返回一组预先存在的值(或属性)来替换算子。这样可以确保该fold方法是真正的“本地”转换,并且可以在不需要模式重写器的情况下调用该方法。

    在 ODS中 ,算子可以设置该hasFolder位以生成fold方法的声明。此方法采用不同的形式,具体取决于算子的结构。

    def MyOp : ... {

      let hasFolder = 1;

    }

    如果算子只有一个结果,则将生成以下内容:

    /// 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<Attribute> operands) {

      ...

    }

    否则,将生成以下内容:

    /// 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<Attribute> operands,

                             SmallVectorImpl<OpFoldResult> &results) {

      ...

    }

    在上面,为每种方法ArrayRef<Attribute>提供了一个与每个算子的常量属性值相对应的。这些算子是实现ConstantLike特征的算子。如果任何一个算子不是常数,则将Attribute提供一个空值。例如,如果提供MYOP三个算子[ a,b,c],但只b是常量,那么operands将是以下形式的[属性(),b值,属性()]。

    同样在上面,使用OpFoldResult。此类表示折叠算子结果的可能结果:SSAValue或 Attribute(对于恒定结果)。如果Value提供了SSA ,则它必须 对应于现有值。该fold方法不允许生成new Value。Attribute返回值的形式没有具体限制 ,但重要的是要确保Attribute 具体表示形式的Type一致性。

    当fold算子上的钩子算子不成功时,语言可以通过实现DialectFoldInterface和覆盖折叠钩子来提供后备功能。

    从属性生成常数 

    当一个fold方法返回aAttribute作为结果时,表示该结果是“常量”。Attribute是该值的常数表示。该fold方法的用户(例如规范化过程)将采用这些Attributes,并在IR中实现常量算子来表示。为了实现这种实现,算子的语言必须实现materializeConstant钩子。该钩子接受一个Attribute 通常由返回的值,fold并产生实现该值的“类似常数”的算子。

    在 ODS中 ,语言可以将hasConstantMaterializer位设置为生成materializeConstant方法的声明。

    def MyDialect_Dialect : ... {

      let hasConstantMaterializer = 1;

    }

    然后可以在源文件中实现常量:

    /// 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) {

      ...

    }

    人工智能芯片与自动驾驶
  • 相关阅读:
    52、saleforce 第一篇
    nodejs自定义模块
    NodeJS require路径
    Angularjs ngTable使用备忘
    HTML5拖拽功能中 dataTransfer对象详解
    Javascript闭包深入解析及实现方法
    Grunt实例
    Grunt插件uglify
    javascript 将字符串当函数执行
    多个springboot项目部署在同一tomcat上,出现jmx错误
  • 原文地址:https://www.cnblogs.com/wujianming-110117/p/14189621.html
Copyright © 2020-2023  润新知