VHLO 方言

什么是 VHLO 方言?

VHLO(有版本控制的 StableHLO)方言用于实现序列化和稳定性。它通过对各个程序元素进行版本控制,提供给定时间点的 StableHLO 方言快照。

VHLO 是一种“仅添加”方言,具有版本化操作、类型和属性,这意味着一旦将某个特征添加到该方言,它便无法以任何影响语义的方式进行修改。

对操作、类型或属性所做的任何更改都需要向方言中添加新版本。例如,如果在 0.9.0 中向 StableHLO 添加了一个假设的 my_op,但在 0.11.0 中进行了更改,则 VHLO 中将包含以下内容:

// This represents the StableHLO version of the op from 0.9.0 -> 0.10.0
// Both the lower and the upper bound of versions are inclusive
def VHLO_MyOpV1 : VHLO_Op<"my_op_v1", "0.9.0", "0.10.0"> {
  let arguments = (ins
    VHLO_AnyType:$operand
  );
  let results = (outs VHLO_AnyType:$result);
}

// This represents the StableHLO version of the op from 0.11.0 -> current
def VHLO_MyOpV2 : VHLO_Op<"my_op_v2", "0.11.0", "current"> {
  let arguments = (ins
    VHLO_AnyType:$operand,
    VHLO_AnyAttr:$attr  // New attribute added to StableHLO in 0.11.0
  );
  let results = (outs VHLO_AnyType:$result);
}

StableHLO 方言只有最新版本的 op。在运行的示例中,v0.11.0 的 StableHLO 方言只有 StableHLO_MyOp 具有 operandattr,而 VHLO 会捕获操作进化的每个阶段。

为什么 VHLO 很有用?

使用版本化方言有助于我们定位先前版本的 StableHLO 运算集。这样封装了 VHLO 方言中操作之间的转换中的前向和向后兼容性。

向前兼容性:向前兼容性是通过转换为 VHLO 并将操作降级为目标版本提供的。如果 VHLO 程序中的每个操作/类型/属性都可以降级为目标版本,则保证可以在运行版本不低于目标版本的使用方时反序列化并转换为 StableHLO,因为此时 VHLO 拥有操作集的快照。

向前兼容性图片

如果使用了先前版本操作中不存在的操作或功能,则此降级转换将失败。这意味着,向前兼容性是在生产方上(而不是在运行时)发现的。

向后兼容性:向后兼容性通过以下方式提供:将 VHLO 操作升级到最新版本(如果需要),然后将操作转换回 StableHLO。兼容性窗口中的所有 VHLO 程序都可以升级到 StableHLO,这意味着不同版本的使用方可以对先前版本中的相同 VHLO 载荷进行反序列化。

向后兼容性图片

更重要的是,VHLO 是在序列化之后的抽象。这意味着机器学习框架(生产方)只需要以 StableHLO 操作为目标,编译器后端(使用方)只需要支持最新版本,即 StableHLO 操作集。与 VHLO 之间的转换由 StableHLO 代码库中维护的机器处理。

MLIR 字节码格式版本

为了保持向前兼容性,StableHLO 版本具有关联的 MLIR 字节码格式版本。此外,最新版本的 StableHLO 将使用最新版本的 MLIR 字节码格式。当 MLIR 字节码格式版本递增时,下一个 StableHLO 版本将递增次要版本,并更新 Version.cpp 以使用最新的 MLIR 字节码格式版本。

如需详细了解 MLIR 字节码格式以及在 StableHLO 中使用该格式的原因,请参阅 bytecode.md

贡献了不兼容的更改

所有涉及兼容性影响的更改都必须通过 RFC 流程完成。这包括添加、弃用或重命名地图项。RFC 获得批准后,以下是一些贡献准则:

提升 Version.h 中的版本号

在更新 VHLO 操作、属性、类型或转化之前,请在 Version.h 中递增次要版本号。添加的任何新 VHLO 功能都将使用此提升版本,例如,提升 0.10.0 --> 0.11.0 后,VhloOps.td 中的新操作将使用:

VHLO_Op<"abs_v2", "0.11.0", "current">

添加所需的 VHLO 实现和转化

集成新功能所需的具体代码会有所不同,但在大多数情况下,需要更改以下内容:

最近的一个兼容性相关提交示例是,在 #1379 中添加了两种 FP8 类型及其在 VHLO 中的实现。

添加 / 更新单元测试

不兼容更改的贡献者负责功能的正面和负面单元测试,以及兼容性单元测试。

兼容性单元测试涉及更新 stablehlo_legalize_to_vhlo.mlir,以确保 StableHLO 使用最新版本的 VHLO 进行往返,以及所需的任何其他向前或向后兼容性测试。

下面是一些示例:

添加带版本控制的序列化测试

将测试点添加到 stablehlo_legalize_to_vhlo.mlir 后,创建名为 stablehlo_legalize_to_vhlo.0_X_0.mlir 的文件的版本化副本,如下所示,以及该文件的字节码版本和 .0_X_0.mlir.bc 扩展名。添加适当的 FileCheck 行以进行向前和向后兼容性测试。

$ export TARGET_VERSION=0.X.0
$ export TARGET_FILENAME=${TARGET_VERSION//./_}
$ cp stablehlo/tests/stablehlo_legalize_to_vhlo.mlir stablehlo/tests/stablehlo_legalize_to_vhlo.$TARGET_FILENAME.mlir
$ build/bin/stablehlo-translate --serialize --target=$TARGET_VERSION --strip-debuginfo stablehlo/tests/stablehlo_legalize_to_vhlo.$TARGET_FILENAME.mlir > stablehlo/tests/stablehlo_legalize_to_vhlo.$TARGET_FILENAME.mlir.bc

# Replace RUN commands in stablehlo/tests/stablehlo_legalize_to_vhlo.0_X_0.mlir with the following for 0.X.0:
// RUN: stablehlo-opt --mlir-print-op-generic %s.bc | FileCheck %s
// RUN: stablehlo-translate --deserialize %s.bc | stablehlo-translate --serialize --target=0.X.0 | stablehlo-opt --mlir-print-op-generic | FileCheck %s
// RUN: diff <(stablehlo-translate --deserialize %s.bc | stablehlo-opt) <(stablehlo-opt --strip-debuginfo %s)
// RUN: diff %s.bc <(stablehlo-translate --serialize --target=0.X.0 --strip-debuginfo %s)

#1430 中的版本化测试示例。