-chlo-legalize-to-stablehlo

Legalizza il flusso di operazioni CHLO in operazioni StableHLO e Shape

-shape-legalize-to-stablehlo

Legalizza le operazioni correlate alla forma in StableHLO.

Un pass sperimentale che legalizza le operazioni correlate alla forma per le operazioni StableHLO.

L'unione di calcoli di forme e dati tramite un passaggio facoltativo consentirà all'ecosistema StableHLO di sfruttare potenzialmente le pipeline di compilazione che utilizzano operazioni StableHLO per modellare il dinamismo.

-stablehlo-canonicalize-dynamism

Canonicalizza le operazioni StableHLO dinamiche in operazioni statiche.

Sostituisce le operazioni StableHLO dinamiche come DynamicReshapeOp con le controparti statiche corrispondenti come DynamicReshapeOp a ReshapeOp o DynamicBroadcastInDim a BroadcastInDim se tutti gli elementi dinamici di queste operazioni sono costanti.

  %c = stablehlo.constant dense<16> : tensor<1xi32>
  %0 = stablehlo.dynamic_broadcast_in_dim %cst, %c, dims = [] : (tensor<f32>, tensor<1xi32>) -> tensor<16xf32>

  ==>

  %0 = stablehlo.broadcast_in_dim %cst, dims = [] : (tensor<f32>) -> tensor<16xf32>

-stablehlo-check-shape-assertions

_Check stablehlo.custom_call @shapeassertion ops.

Convalida le chiamate personalizzate shape_assertion.

Le asserzioni di forma convalidano i vincoli sulle dimensioni dinamiche in StableHLO. Ad esempio, se un framework deve applicare un vincolo di DimA < 2, potrebbe essere emesso il seguente IR:

%dimA = <get_dimension_size or input arg> : tensor<i32>
%c2 = stablehlo.constant dense<2> : tensor<i32>
%is_lt = stablehlo.compare LT %dimA, %c2 : tensor<i1>
stablehlo.custom_call @shape_assertion(%is_lt) { error_message = "DimA must be less than 2" }

Dopo il passaggio, se le forme sono corrette, il stablehlo.custom_call verrà rimosso.

Opzioni

-enable-shape-assertions : Whether shape assertions may generate errors.

-stablehlo-compatibility-expander

Expander di compatibilità per le operazioni StableHLO.

Le operazioni StableHLO vengono aggiornate o viene introdotta una nuova operazione nelle versioni più recenti. Questo pass di attivazione espande la compatibilità con le versioni precedenti di StableHLO decomponendo le operazioni StableHLO più recenti in operazioni equivalenti supportate da queste versioni precedenti.

Perché si tratta di un abbonamento ad attivazione facoltativa?

Di tanto in tanto, vengono utilizzati miglioramenti di StableHLO per semplificare notevolmente la gestione di determinati pattern comuni nell'ecosistema OpenXLA. Ciò include elementi come TanOp, che offre un elevato supporto per framework e compilatori, nonché dimensioni di batching di raccolta/distribuzione, che possono essere rappresentate utilizzando sezioni, ma rendono lo sharding molto più difficile. Per questa categoria di nuove funzionalità, non offriamo il downgrade automatico, in quanto potrebbe eliminare informazioni importanti utilizzate nelle ottimizzazioni successive. Questo passaggio può essere utilizzato per espandere queste operazioni in base a una versione target per massimizzare la compatibilità a scapito di una compilazione potenzialmente meno ottimale.

func.func @tan_op_non_complex(%arg0: tensor<4xf64>) -> tensor<4xf64> {
  %1 = stablehlo.tan %arg0 : tensor<4xf64>
  func.return %1 : tensor<4xf64>
}

==>

func.func @tan_op_non_complex(%arg0: tensor<4xf64>) -> tensor<4xf64> {
  %0 = stablehlo.sine %arg0 : tensor<4xf64>
  %1 = stablehlo.cosine %arg0 : tensor<4xf64>
  %2 = stablehlo.divide %0, %1 : tensor<4xf64>
  return %2 : tensor<4xf64>
}

Opzioni

-target : The target version. Must be a version of the form #.#.#.

-stablehlo-complex-math-expander

Expander per operazioni matematiche complesse di StableHLO.

Le operazioni matematiche complesse di StableHLO sono decomposizioni che utilizzano operazioni matematiche reali di StableHLO.

Questa affermazione si basa sul presupposto che non esista hardware che supporti nativamente numeri complessi o operazioni matematiche complesse. Ciò significa che i meccanismi di riserva per operazioni matematiche complesse che i compilatori possono implementare sono ridondanti. Se attivi questo passaggio, tutte le operazioni matematiche complesse di StableHLO verranno espansi.

func.func @sqrt_op_complex(%arg0: tensor<4xcomplex<f64>>) -> tensor<4xcomplex<f64>> {
  %1 = stablehlo.sqrt %arg0 : tensor<4xcomplex<f64>>
  func.return %1 : tensor<4xcomplex<f64>>
}

==>

func.func @sqrt_op_complex(%arg0: tensor<4xcomplex<f64>>) -> tensor<4xcomplex<f64>> {
  TBD
  return %2 : tensor<4xcomplex<f64>>
}

-stablehlo-convert-to-signless

Passaggio per trasformare la rappresentazione intermedia in numeri interi senza segno.

-stablehlo-legalize-composite-to-call

Sostituisce le operazioni composite con una chiamata alla loro scomposizione.

Sostituisce le operazioni composite con una chiamata alla loro scomposizione, ad esempio:

stablehlo.composite "my_namespace.my_op" %arg0, %arg1 {
  decomposition = @bar,
  version = 1,
  composite_attributes = {
    "my_attribute": "my_value"
  }
}

Diventerà:

func.call @bar(%arg0, %arg1)

Un sottoinsieme di compositi può essere escluso da questa trasformazione utilizzando il flag "except", ad esempio:

stablehlo-opt --stablehlo-legalize-composite-to-call=except='foo.baz,foo.qux'

Opzioni

-except : Names of composites that should not be replaced with calls.

-stablehlo-legalize-deprecated-ops

Legalizza le operazioni ritirate in operazioni ben supportate.

La RFC relativa al ritiro di Opset StableHLO v1.0 (#2283) propone di rimuovere diverse operazioni ridondanti. Questo passaggio consente di valutare l'impatto di queste rimozioni in varie pipeline di compilazione legalizzandole con le loro controparti supportate a lungo termine.

Opzioni

-fail-on-unused : Fail on (mostly) unused ops that are deprecated without any fallback.

-stablehlo-legalize-qdq-to-quantized-op

Unire il pattern (dequantizzazione, operazione in virgola mobile e quantizzazione) nell'operazione quantizzata StableHLO

Unisci (dequantizza, operazione in virgola mobile e quantizza) il pattern nell'operazione quantizzata StableHLO Nota: il passaggio non elimina alcuna operazione preesistente. Ad esempio, il seguente programma

func.func @add(%arg0: tensor<16x16x!quant.uniform<ui8:f32, 34.0:16>>) -> tensor<16x16x!quant.uniform<ui8:f32, 34.0:16>> {
  %0 = stablehlo.uniform_dequantize %arg0 : (tensor<16x16x!quant.uniform<ui8:f32, 34.0:16>>) -> tensor<16x16xf32>
  %1 = stablehlo.abs %0 : tensor<16x16xf32>
  %2 = stablehlo.uniform_quantize %1 : (tensor<16x16xf32>) -> tensor<16x16x!quant.uniform<ui8:f32, 34.0:16>>
  func.return %2 : tensor<16x16x!quant.uniform<ui8:f32, 34.0:16>>
}

Diventerà:

func.func @add(%arg0: tensor<16x16x!quant.uniform<u8:f32, 3.400000e+01:16>>) -> tensor<16x16x!quant.uniform<u8:f32, 3.400000e+01:16>> {
  %0 = stablehlo.uniform_dequantize %arg0 : (tensor<16x16x!quant.uniform<u8:f32, 3.400000e+01:16>>) -> tensor<16x16xf32>
  %1 = stablehlo.abs %0 : tensor<16x16xf32>
  %2 = stablehlo.abs %arg0 : tensor<16x16x!quant.uniform<u8:f32, 3.400000e+01:16>>
  %3 = stablehlo.uniform_quantize %1 : (tensor<16x16xf32>) -> tensor<16x16x!quant.uniform<u8:f32, 3.400000e+01:16>>
  return %2 : tensor<16x16x!quant.uniform<u8:f32, 3.400000e+01:16>>
}

-stablehlo-legalize-quant-to-math

Converti le operazioni quantizzate di StableHLO in operazioni matematiche primitive di StableHLO.

Converti i programmi StableHLO che utilizzano tipi UniformQuantized in operazioni matematiche con numeri interi semanticamente equivalenti.

func.func @add(%arg0: tensor<!quant.uniform<i8:f32,1.0:0>>, %arg1: tensor<!quant.uniform<i8:f32,2.0:1>>) ->  tensor<!quant.uniform<i8:f32,3.0:2>> {
  %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<!quant.uniform<i8:f32,1.0:0>>, tensor<!quant.uniform<i8:f32,2.0:1>>) -> tensor<!quant.uniform<i8:f32,3.0:2>>
  func.return %0 : tensor<!quant.uniform<i8:f32,3.0:2>>
}

Diventerà:

func.func @add(%arg0: tensor<i8>, %arg1: tensor<i8>) -> tensor<i8> {
  %0 = stablehlo.convert %arg0 : (tensor<i8>) -> tensor<f32>
  %cst = stablehlo.constant dense<0.333333343> : tensor<f32>
  %1 = chlo.broadcast_multiply %0, %cst : (tensor<f32>, tensor<f32>) -> tensor<f32>
  %cst_0 = stablehlo.constant dense<2.000000e+00> : tensor<f32>
  %2 = chlo.broadcast_add %1, %cst_0 : (tensor<f32>, tensor<f32>) -> tensor<f32>
  %3 = stablehlo.round_nearest_even %2 : tensor<f32>
  %4 = stablehlo.convert %3 : (tensor<f32>) -> tensor<i32>
  %5 = stablehlo.convert %arg1 : (tensor<i8>) -> tensor<f32>
  %cst_1 = stablehlo.constant dense<0.666666686> : tensor<f32>
  %6 = chlo.broadcast_multiply %5, %cst_1 : (tensor<f32>, tensor<f32>) -> tensor<f32>
  %cst_2 = stablehlo.constant dense<1.33333337> : tensor<f32>
  %7 = chlo.broadcast_add %6, %cst_2 : (tensor<f32>, tensor<f32>) -> tensor<f32>
  %8 = stablehlo.round_nearest_even %7 : tensor<f32>
  %9 = stablehlo.convert %8 : (tensor<f32>) -> tensor<i32>
  %c = stablehlo.constant dense<2> : tensor<i32>
  %10 = chlo.broadcast_add %4, %9 : (tensor<i32>, tensor<i32>) -> tensor<i32>
  %11 = chlo.broadcast_subtract %10, %c : (tensor<i32>, tensor<i32>) -> tensor<i32>
  %c_3 = stablehlo.constant dense<-128> : tensor<i32>
  %c_4 = stablehlo.constant dense<127> : tensor<i32>
  %12 = stablehlo.clamp %c_3, %11, %c_4 : tensor<i32>
  %13 = stablehlo.convert %12 : (tensor<i32>) -> tensor<i8>
  return %13 : tensor<i8>
}

-stablehlo-legalize-quantized-op-to-qdq

Decomponi l'operazione StableHLO quantizzata in uno schema (dequantizzazione, operazione in virgola mobile e quantizzazione).

Decomponi i programmi quantizzati StableHLO utilizzando operazioni di quantizzazione/dequantizzazione uniformi. Ad esempio, il seguente programma

func.func @add(%arg0: tensor<!quant.uniform<i8:f32,1.0:0>>, %arg1: tensor<!quant.uniform<i8:f32,2.0:1>>) ->  tensor<!quant.uniform<i8:f32,3.0:2>> {
  %0 = "stablehlo.add"(%arg0, %arg1) : (tensor<!quant.uniform<i8:f32,1.0:0>>, tensor<!quant.uniform<i8:f32,2.0:1>>) -> tensor<!quant.uniform<i8:f32,3.0:2>>
  func.return %0 : tensor<!quant.uniform<i8:f32,3.0:2>>
}

Diventerà:

func.func @add(%arg0: tensor<!quant.uniform<i8:f32, 1.000000e+00>>, %arg1: tensor<!quant.uniform<i8:f32, 2.000000e+00:1>>) -> tensor<!quant.uniform<i8:f32, 3.000000e+00:2>> {
  %0 = stablehlo.uniform_dequantize %arg0 : (tensor<!quant.uniform<i8:f32, 1.000000e+00>>) -> tensor<f32>
  %1 = stablehlo.uniform_dequantize %arg1 : (tensor<!quant.uniform<i8:f32, 2.000000e+00:1>>) -> tensor<f32>
  %2 = stablehlo.add %0, %1 : tensor<f32>
  %3 = stablehlo.uniform_quantize %2 : (tensor<f32>) -> tensor<!quant.uniform<i8:f32, 3.000000e+00:2>>
  return %3 : tensor<!quant.uniform<i8:f32, 3.000000e+00:2>>
}

-stablehlo-legalize-to-vhlo

Legalizza StableHLO in VHLO.

Esegui la legalizzazione di StableHLO all'ultima versione delle operazioni in VHLO. Queste operazioni possono poi essere eseguite il downgrade a versioni precedenti di VHLO per la compatibilità futura utilizzando VhloToVersionPass.

stablehlo.exponential %[[ARG0]] <{result_accuracy = DEFAULT}> : tensor<f32>
# ====>
"vhlo.exponential_v2"(%[[ARG0]]) <{result_accuracy = #vhlo.DEFAULT_v1}> : !vhlo.tensor_v1<!vhlo.f32_v1>

Consulta vhlo.md > The VHLO dialect per informazioni dettagliate su come VHLO viene utilizzato per preservare la compatibilità in avanti e all'indietro.

Opzioni

-allow-other-dialects : Allow serialization to use other (potentially unstable) dialects, inserts unrealized casts between dialects.

-stablehlo-refine-arguments

Perfeziona le forme degli argomenti della funzione principale.

Modifica gli argomenti della funzione principale utilizzando la firma del tipo di input. Racchiude gli argomenti in custom_call @stablehlo.shape_refinement_operand_wrapper per mantenere valido l'IR prima dell'esecuzione del perfezionamento della forma.

func.func public @main(%arg0: tensor<?xf32>) -> tensor<?xf32> {
  ...
}

==>

func.func public @main(%arg0: tensor<16xf32>) -> tensor<?xf32> {
  %c = stablehlo.constant dense<16> : tensor<1xi64>
  %0 = stablehlo.custom_call @stablehlo.shape_refinement_operand_wrapper(%arg0, %c) {...}
    : (tensor<16xf32>, tensor<1xi64>) -> tensor<?xf32>
  ...
}

refinedTypesOption può essere utilizzato per specificare un elenco di tipi raffinati. Può essere specificato in MLIR con --types='tensor<...>,tensor<...>' o trasmesso al metodo di creazione della tessera. L'elenco dei tipi di perfezionamento deve specificare il tipo di ogni argomento del metodo main che viene perfezionato.

Opzioni

-types : The new types to be used for the main function's arguments, specified as an MLIR TypeRange 'tensor<1x2xf32>, ...'

-stablehlo-refine-shapes

Perfeziona le forme in un programma StableHLO.

Descrive un programma StableHLO che perfeziona le forme all'interno delle operazioni.

Il caso d'uso principale di questa passata è la specializzazione di programmi con forme dinamiche in forme statiche. Se un programma StableHLO con forma dinamica ha la struttura corretta, l'aggiornamento dei tipi di argomenti da forme dinamiche a forme statiche e l'esecuzione di questa passata propagheranno le forme statiche nel programma.

Questo passaggio rimuove custom_call @shape_refinement_operand_wrapper sostituendo gli utilizzi del risultato direttamente con l'operando e propaga le forme statiche in tutto il programma.

  %c = stablehlo.constant dense<16> : tensor<1xi64>
  %0 = stablehlo.custom_call @stablehlo.shape_refinement_operand_wrapper(%arg0, %c) {...}
      : (tensor<16xf32>, tensor<1xi64>) -> tensor<?xf32>
  %1 = stablehlo.add %0, %0 : tensor<?xf32>

  ==>

  %1 = stablehlo.add %arg0, %arg0 : tensor<16xf32>

I moduli validi per il perfezionamento della forma devono avere le seguenti proprietà:

  • Tutte le forme dinamiche dipendono solo dalle forme di input (nessuna dipendenza della forma dai contenuti dell'array di input). Le operazioni che dipendono transitivamente solo dalle forme di input (ad es. come indicato da stablehlo.get_dimension_size) o da costanti globali come i valori risolti di numeri interi simbolici (ovvero tensore : A = 5) sono chiamate operazioni dimension. Tutti i valori delle dimensioni possono essere risolti in costanti tramite l'ottimizzazione delle costanti interprocedurale.
  • Le funzioni intermedie possono accettare un numero di argomenti token (di tipo !stablehlo.token) all'inizio dell'elenco degli argomenti, seguiti da alcuni argomenti costanti globali che sono scalari interi costanti, come i valori risolti di numeri interi simbolici (ad es. tensore : A = 5).
  • Alcune funzioni intermedie potrebbero restituire calcoli su costanti globali, ad esempio floordiv su valori symint. Queste funzioni sono indicate solo restituendo valori costanti dopo il perfezionamento. Queste funzioni sono inlined.
  • Tutte le chiamate a una singola funzione vengono risolte con le stesse forme di argomenti e non vengono effettuate chiamate di funzioni ricorsive / corecursive.

-stablehlo-wrap-in-composite

Racchiude un'operazione StableHLO non composita in un'operazione composita.

Esegue il wrapping delle operazioni StableHLO nelle operazioni stablehlo.composite.

Ad esempio, considera un semplice programma StableHLO:

func.func @main(%arg0 : tensor<2xf32>, %arg1 : tensor<2xf32>) -> tensor<2xf32> {
  %0 = stablehlo.add %arg0, %arg1 : tensor<2xf32>
  return %0 : tensor<2xf32>
}

L'applicazione di questa passata alle operazioni di avvolgimento stablehlo.add comporterà il seguente programma:

func.func @main(%arg0: tensor<2xf32>, %arg1: tensor<2xf32>) -> tensor<2xf32> {
  %0 = stablehlo.composite "stablehlo.add" %arg0, %arg1 {decomposition = @stablehlo.add.impl} : (tensor<2xf32>, tensor<2xf32>) -> tensor<2xf32>
  return %0 : tensor<2xf32>
}
func.func private @stablehlo.add.impl(%arg0: tensor<2xf32>, %arg1: tensor<2xf32>) -> tensor<2xf32> {
  %0 = stablehlo.add %arg0, %arg1 : tensor<2xf32>
  return %0 : tensor<2xf32>
}

Note:

  • L'attributo name dell'operazione stablehlo.composite generata sarà sempre uguale al nome dell'operazione originale sottoposta a wrapping (ad es. se esegui il wrapping di un'operazione stablehlo.add, anche l'operazione composita si chiamerà "stablehlo.add").
  • La funzione privata che incapsula l'operazione originale (a cui fa riferimento l'attributo decomposition dell'operazione stablehlo.composite) verrà denominata utilizzando il pattern <op_name>.impl[.N], dove <op_name> è il nome dell'operazione originale e N è un identificatore intero univoco generato per evitare conflitti di denominazione all'interno del modulo.

Questa tessera può essere utilizzata in due modi distinti:

Modalità 1: utilizzo della riga di comando

Questa modalità è pensata per il debug o il test, in quanto offre un controllo minimo sugli attributi delle operazioni stablehlo.composite generate. Esegue il wrapping di tutte le istanze delle operazioni specificate utilizzando le opzioni op-names (un elenco di nomi di operazioni separati da virgole). Gli attributi dell'operazione stablehlo.composite appena creata saranno gli stessi dell'operazione originale.

Esempio di utilizzo:

stablehlo-opt input.mlir --stablehlo-wrap-in-composite=op-names='stablehlo.add,stablehlo.mul' -o output.mlir

Modalità 2: wrapping programmatico a livello di modulo con gestione personalizzata degli attributi

Questa modalità estende il wrapping programmatico all'intero modulo, offrendo un controllo granulare sulle operazioni sottoposte a wrapping e sui relativi attributi. Ciò si ottiene utilizzando l'API createStablehloWrapInCompositePass, che accetta un CompositeAttributeProviderMap come argomento.

CompositeAttributeProviderMap è una mappa che indica quali operazioni devono essere prese in considerazione per il wrapping e come devono essere gestiti i relativi attributi. La sua semantica è la seguente:

  • Chiavi (mlir::TypeID): TypeID di un'operazione MLIR. Se TypeID di un'operazione corrisponde a una chiave nella mappa, diventa un candidato per il wrapping.
  • Valori (funzioni Lambda): funzione Lambda di tipo std::function<std::optional<NamedAttrList>(Operation*)>. Questa funzione viene applicata a ogni operazione candidata.
    • Input:un mlir::Operation*, ovvero un'istanza del tipo di operazione corrispondente alla chiave TypeID.
    • Valore restituito:un std::optional<NamedAttrList>.
      • Se la lambda restituisce un NamedAttrList (racchiuso in std::optional), l'operazione viene racchiusa in un'operazione stablehlo::composite e gli attributi restituiti vengono utilizzati per impostare gli attributi del composito.
      • Se la lambda restituisce std::nullopt, l'operazione non viene incapsulata. Ciò consente il wrapping selettivo in base a criteri personalizzati.

Esempio (C++):


stablehlo::CompositeAttributeProviderMap compositeAttributeProviderMap;

compositeAttributeProviderMap[mlir::TypeID::get<mlir::stablehlo::AddOp>()] =
  [](mlir::Operation* op) -> std::optional<mlir::NamedAttrList> {
  // Custom logic to determine if and how to wrap the operation.
  // Example: Only wrap if it's on a specific type.
  if (mlir::isa<mlir::Float32Type>(op->getOperand(0).getType())) {
    return mlir::NamedAttrList(op->getAttrs());
  }
  return std::nullopt; // Do not wrap.
};

pm.addPass(createStablehloWrapInCompositePass(compositeAttributeProviderMap, compositeVersion));
if (mlir::failed(pm.run(module))) {
  return;
}

Opzioni

-op-names : The names of the ops to wrap.
-version  : The version number of the composite op.

-vhlo-legalize-to-stablehlo

Legalizzare VHLO in StableHLO.

-vhlo-to-version

Converti tra le versioni di VHLO per la compatibilità.

Esegue la conversione tra le versioni di VHLO per l'upgrade e il downgrade dell'IR per preservare la compatibilità con le versioni precedenti e successive.

"vhlo.exponential_v2"(%[[ARG0]]) <{result_accuracy = DEFAULT}>
# ==( -target=1.0.0 )==>
"vhlo.exponential_v1"(%[[ARG0]])
# ==( -target=1.9.0 )==>
"vhlo.exponential_v2"(%[[ARG0]]) <{result_accuracy = DEFAULT}>

Consulta vhlo.md > The VHLO dialect per informazioni dettagliate su come VHLO viene utilizzato per preservare la compatibilità in avanti e all'indietro.

Opzioni

-target : The target version. Must be a version of the form #.#.# .