El dialecto VHLO

¿Qué es el dialecto VHLO?

El dialecto VHLO (Versioned StableHLO) se usa para la serialización y la estabilidad. Proporciona una instantánea del dialecto StableHLO en un momento determinado mediante el control de versiones de los elementos individuales del programa.

VHLO es un dialecto de solo agregar con operaciones con versiones, tipos y atributos, lo que significa que una vez que se agrega una característica al dialecto, no se puede modificar de ninguna manera que afecte la semántica.

Cualquier cambio en una operación, tipo o atributo requiere que se agregue una versión nueva al dialecto. Por ejemplo, si se agregara un my_op hipotético al StableHLO en 0.9.0, pero se cambiara en 0.11.0, tendríamos lo siguiente en 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);
}

El dialecto StableHLO solo tiene la última versión de las operaciones. En el ejemplo en ejecución, el dialecto estable en la versión v0.11.0 solo tendría el StableHLO_MyOp que tiene operand y attr, mientras que VHLO captura cada fase de la evolución de la operación.

¿Por qué es útil VHLO?

Tener un dialecto con control de versiones nos permite orientarnos a versiones anteriores del opset estable. Esto encapsula la compatibilidad hacia delante y hacia atrás en las conversiones entre operaciones en el dialecto VHLO.

Compatibilidad con versiones posteriores: La compatibilidad con versiones anteriores se proporciona mediante la conversión a VHLO y el cambio de operaciones a una versión inferior. Si cada op/tipo/attr en un programa de VHLO se puede cambiar a una versión inferior de destino, se garantiza que se podrá deserializar y convertir a StableHLO en un consumidor que ejecute una versión superior o igual a la versión objetivo, ya que VHLO tiene una instantánea del conjunto de operaciones en ese momento.

Imagen de compatibilidad con versiones posteriores

Esta conversión a una versión inferior fallará si se usan operaciones o funciones que no existen en la versión anterior del conjunto de operaciones. Esto significa que la compatibilidad con versiones futuras se descubre en el productor, en lugar de en el tiempo de ejecución.

Retrocompatibilidad: La retrocompatibilidad se proporciona actualizando las operaciones de VHLO a su versión más reciente (si es necesario) y, luego, convirtiendo una op de vuelta a EstableHLO. Todos los programas de VHLO dentro de la ventana de compatibilidad se pueden actualizar a StableHLO, lo que significa que las diferentes versiones de los consumidores pueden deserializar la misma carga útil de VHLO de una versión anterior.

Imagen de retrocompatibilidad

Lo que es más importante, VHLO se abstrae de la serialización. Esto significa que los frameworks de AA (productores) solo necesitan orientarse a las operaciones estables, y los backends del compilador (consumidores) solo deben admitir la versión más reciente, que es el conjunto de operaciones estable. Las conversiones hacia y desde VHLO se encargan de la maquinaria que se mantiene en el repositorio StableHLO.

Versiones de formato de código de bytes MLIR

Para mantener la compatibilidad con versiones posteriores, las versiones StableHLO tienen asociada una versión de formato de código de bytes MLIR. Además, la versión más reciente de StableHLO usará la última versión del formato de código de bytes MLIR. Cuando se incrementa la versión del formato de código de bytes de MLIR, la próxima versión de StableHLO aumentará la versión secundaria y actualizará Version.cpp para usar la versión más reciente del formato de código de bytes de MLIR.

Para obtener más información sobre el formato de código de bytes MLIR y la lógica para usarlo en StableHLO, consulta bytecode.md.

Cómo contribuir con cambios incompatibles

Todos los cambios con implicaciones de compatibilidad deben someterse al proceso de RFC. Esto incluye agregar o dar de baja un componente, o cambiarle el nombre. Una vez que se aprueba el RFC, estos son algunos lineamientos de contribución:

Modifica el número de versión en Version.h.

Antes de actualizar las operaciones, los atributos, los tipos o las conversiones de VHLO, aumenta el número de versión secundario en Version.h. Cualquier función nueva de VHLO que se agregue usaría esta versión mejorada. Por ejemplo, después de generar 0.10.0 --> 0.11.0, una operación nueva en VhloOps.td usaría lo siguiente:

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

Agrega las conversiones y la implementación de VHLO obligatorias

El código exacto necesario para integrar una función nueva será variable, pero en la mayoría de los casos se deberá modificar lo siguiente:

Un ejemplo reciente de una presentación relacionada con la compatibilidad fue la adición de dos tipos de FP8, así como su implementación en VHLO en #1379.

Agrega o actualiza pruebas de unidades

El colaborador que genera un cambio incompatible es responsable de las pruebas de unidades positivas y negativas de la función, así como de las pruebas de unidades de compatibilidad.

La prueba de unidades de compatibilidad implica actualizar stablehlo_legalize_to_vhlo.mlir para garantizar que StableHLO realice la ida y vuelta con la versión más reciente de VHLO, así como cualquier prueba adicional de retrocompatibilidad requerida.

Estos son algunos ejemplos:

Agregar prueba de serialización con versiones

Después de agregar un punto de prueba a stablehlo_legalize_to_vhlo.mlir, crea una copia con versión del archivo llamada stablehlo_legalize_to_vhlo.0_X_0.mlir de la siguiente manera, junto con una versión de código de bytes de ese archivo con la extensión .0_X_0.mlir.bc. Agrega las líneas de FileCheck adecuadas para las pruebas de compatibilidad con versiones anteriores y posteriores.

$ 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)

Ejemplo de prueba con versión en #1430.