Il dialetto VHLO

Cos'è il dialetto VHLO?

Il dialetto VHLO (Versioned StableHLO) viene utilizzato per la serializzazione e la stabilità. Fornisce uno snapshot del dialetto StableHLO in un determinato momento mediante il controllo delle versioni dei singoli elementi del programma.

VHLO è un dialetto di sola aggiunta con operazioni, tipi e attributi sottoposti a versione. Ciò significa che, una volta aggiunta una funzionalità al dialetto, non può essere modificata in alcun modo che influisca sulla semantica.

Qualsiasi modifica a un'operazione, un tipo o un attributo richiede l'aggiunta di una nuova versione al dialetto. Ad esempio, se un my_op ipotetico è stato aggiunto a StableHLO nella versione 0.9.0, ma è stato modificato nella versione 0.11.0, avremmo quanto segue in 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);
}

Il dialetto StableHLO ha solo la versione più recente delle operazioni. Nell'esempio in esecuzione, il dialetto StableHLO nella v0.11.0 avrà solo StableHLO_MyOp che ha operand e attr, mentre VHLO acquisisce ogni fase dell'evoluzione dell'operazione.

Perché VHLO è utile?

Un dialetto con il controllo delle versioni consente di scegliere come target le versioni precedenti dell'opset stabileHLO. Questo incapsula la compatibilità in avanti e indietro nelle conversioni tra le operazioni nel dialetto VHLO.

Compatibilità in avanti:la compatibilità in avanti è fornita convertendo in VHLO ed eseguendo il downgrade delle operazioni a una versione di destinazione. Se è possibile eseguire il downgrade di ogni op/type/attr in un programma VHLO alla versione di destinazione, è garantito che sia deserializzabile e convertibile in StableHLO su un consumer che esegue una versione maggiore o uguale alla versione di destinazione, poiché VHLO dispone di uno snapshot dell'opset in quel momento.

Immagine compatibilità Inoltra

Questa conversione di downgrade avrà esito negativo se vengono utilizzate operazioni o funzionalità che non esistono nella versione precedente dell'opset. Ciò significa che la compatibilità in tempo reale viene rilevata sul producer, anziché in fase di runtime.

Compatibilità con le versioni precedenti: la compatibilità con le versioni precedenti viene fornita eseguendo l'upgrade delle operazioni VHLO alla versione più recente (se necessario), quindi convertendo un'operazione in StableHLO. Tutti i programmi VHLO all'interno della finestra di compatibilità sono aggiornabili a StableHLO, il che significa che diverse versioni dei consumer possono deserializzare lo stesso payload VHLO da una versione precedente.

Immagine compatibilità con le versioni precedenti

Ancora più importante, VHLO è astratto alla base della serializzazione. Ciò significa che i framework ML (produttori) devono scegliere come target solo le operazioni StableHLO, mentre i backend del compilatore (consumer) devono supportare solo la versione più recente, ovvero il set operativo StableHLO. Le conversioni da e verso VHLO vengono gestite con i macchinari gestiti nel repository StableHLO.

Versioni formato byte MLIR

Per mantenere la compatibilità con il futuro, le versioni StableHLO hanno una versione associata per il formato byte MLIR. Inoltre, la versione più recente di StableHLO utilizzerà la versione più recente del formato bytecode MLIR. Quando la versione del formato byte MLIR viene incrementata, la release successiva di StableHLO incrementerà la versione secondaria e aggiornerà Version.cpp in modo che utilizzi l'ultima versione del formato byte MLIR.

Per maggiori dettagli sul formato Bytecode MLIR e sulla motivazione per il suo utilizzo in StableHLO, consulta bytecode.md.

Contributo a modifiche incompatibili

Tutte le modifiche con implicazioni di compatibilità devono passare attraverso il processo RFC. Ciò include l'aggiunta, la deprecazione o la ridenominazione di un elemento. Una volta approvata la RFC, ecco alcune linee guida per i contributi:

Esegui il Bump del numero di versione in Version.h

Prima di aggiornare operazioni, attributi, tipi o conversioni di VHLO, incrementa il numero di versione minore in Version.h. Qualsiasi nuova funzionalità VHLO aggiunta utilizzerà questa versione protetta. Ad esempio, dopo aver eseguito il buffering di 0.10.0 --> 0.11.0, una nuova operazione in VhloOps.td userebbe:

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

Aggiungi implementazione e conversioni VHLO richiesta

Il codice esatto necessario per integrare una nuova funzionalità varia, ma nella maggior parte dei casi è necessario modificare quanto segue:

Un esempio recente di invio relativo alla compatibilità è stata l'aggiunta di due tipi FP8, nonché la loro implementazione in VHLO in #1379.

Aggiungi / aggiorna test delle unità

Il collaboratore di una modifica incompatibile è responsabile dei test delle unità positive e negative della funzionalità, nonché dei test delle unità di compatibilità.

Il test delle unità di compatibilità comporta l'aggiornamento di stablehlo_legalize_to_vhlo.mlir per garantire che StableHLO andata e ritorno con la versione più recente di VHLO, nonché eventuali ulteriori test di compatibilità in avanti o indietro richiesti.

Ecco alcuni esempi:

Aggiungi test di serializzazione con più versioni

Dopo aver aggiunto un punto di test a stablehlo_legalize_to_vhlo.mlir, crea una copia del controllo delle versioni del file denominato stablehlo_legalize_to_vhlo.0_X_0.mlir come segue, insieme a una versione in bytecode del file con un'estensione .0_X_0.mlir.bc. Aggiungi righe di FileCheck corrette per i test di compatibilità con le versioni precedenti e future.

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

Esempio di test del controllo delle versioni in #1430.