O dialeto VHLO

O que é o dialeto VHLO?

O dialeto VHLO (StableHLO com controle de versão) é usado para serialização e estabilidade. Ele fornece um resumo do dialeto StableHLO em um determinado momento pelo controle de versões de elementos individuais do programa.

VHLO é um dialeto somente para adição com operações, tipos e atributos com controle de versão, o que significa que, depois que um elemento é adicionado ao dialeto, ele não pode ser modificado de nenhuma maneira que afete a semântica.

Qualquer mudança em uma op, tipo ou atributo exige que uma nova versão seja adicionada ao dialeto. Por exemplo, se um my_op hipotético foi adicionado ao StableHLO na versão 0.9.0, mas alterado na 0.11.0, teríamos o seguinte na 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);
}

O dialeto StableHLO tem apenas a versão mais recente do ops. No exemplo em execução, o dialeto StableHLO na v0.11.0 teria apenas o StableHLO_MyOp que tem operand e attr, enquanto a VHLO captura cada fase da evolução da operação.

Por que a VHLO é útil?

Ter um dialeto com controle de versão nos permite segmentar versões anteriores do conjunto de opset StableHLO. Isso encapsula a compatibilidade com versões anteriores e posteriores em conversões entre operações no dialeto VHLO.

Compatibilidade futura:a compatibilidade com versões futuras é fornecida pela conversão para VHLO e downgrade das operações para uma versão de destino. Se cada op/tipo/attr em um programa VHLO puder ser rebaixado para a versão de destino, será garantido que ele será desserializável e conversível para StableHLO em um consumidor que estiver executando uma versão maior ou igual à versão de destino, já que a VHLO tem um snapshot do opset naquele momento.

Imagem de compatibilidade com versões futuras

Essa conversão de downgrade falhará se operações ou recursos que não existem na versão anterior da opset forem usados. Isso significa que a compatibilidade com versões futuras é descoberta no produtor, e não durante a execução.

Compatibilidade com versões anteriores:a compatibilidade com versões anteriores é fornecida ao fazer o upgrade das operações de VHLO para a versão mais recente (se necessário) e, em seguida, converter uma operação de volta para o StableHLO. Todos os programas VHLO na janela de compatibilidade podem ser atualizados para StableHLO, o que significa que diferentes versões de consumidores podem desserializar o mesmo payload de VHLO de uma versão anterior.

Imagem de compatibilidade com versões anteriores

Mais importante, a VHLO é abstraída por trás da serialização. Isso significa que os frameworks de ML (produtores) só precisam segmentar operações StableHLO, e os back-ends do compilador (consumidores) só precisam oferecer suporte à versão mais recente, que é o conjunto de operações StableHLO. As conversões de e para VHLO são processadas com máquinas mantidas no repositório StableHLO.

Versões do formato Bytecode MLIR

Para manter a compatibilidade com versões futuras, as versões StableHLO têm uma versão associada do formato MLIR Bytecode Format. Além disso, a versão mais recente do StableHLO usará a versão mais recente do formato Bytecode MLIR. Quando a versão do formato MLIR Bytecode Format for incrementada, a próxima versão do StableHLO incrementará a versão secundária e atualizará o Version.cpp para usar a versão mais recente do formato MLIR Bytecode Format.

Para detalhes sobre o formato Bytecode MLIR e a lógica para usá-lo em StableHLO, consulte bytecode.md.

Contribuição de alterações incompatíveis

Todas as mudanças com implicações de compatibilidade precisam passar pelo processo de RFC. Isso inclui adicionar, descontinuar ou renomear um recurso. Depois que o RFC for aprovado, veja algumas diretrizes de contribuição:

Melhore o número da versão em Version.h

Antes de atualizar as operações, os atributos, os tipos ou as conversões da VHLO, incremente o número da versão secundária em Version.h. Qualquer novo recurso de VHLO adicionado usaria essa versão aprimorada. Por exemplo, depois de atingir 0.10.0 --> 0.11.0, uma nova operação em VhloOps.td usaria:

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

Adicione a implementação e as conversões necessárias da VHLO

O código exato necessário para integrar um novo recurso varia, mas, na maior parte, será necessário mudar o seguinte:

Um exemplo recente de um envio relacionado à compatibilidade foi a adição de dois tipos FP8, bem como a implementação deles na VHLO em #1379 (link em inglês).

Adicionar / atualizar testes de unidade

O colaborador de uma mudança incompatível é responsável por testes de unidade positivos e negativos do recurso, bem como testes de unidade de compatibilidade.

O teste de unidade de compatibilidade envolve a atualização de stablehlo_legalize_to_vhlo.mlir para garantir que o StableHLO use a versão mais recente da VHLO, bem como todos os outros testes de compatibilidade necessários.

Alguns exemplos:

Adicionar teste de serialização com controle de versões

Depois de adicionar um ponto de teste a stablehlo_legalize_to_vhlo.mlir, crie uma cópia com controle de versão do arquivo chamado stablehlo_legalize_to_vhlo.0_X_0.mlir, conforme a seguir, junto com uma versão de bytecode desse arquivo com uma extensão .0_X_0.mlir.bc. Adicione linhas adequadas do FileCheck para testes de compatibilidade com versões anteriores e futuras.

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

Exemplo de teste com controle de versão em #1430.