StableHLO è stato originariamente generato con il bootstrapping dal dialetto MHLO e ha ereditato l'implementazione MHLO dell'inferenza dei tipi. L'avanzamento dell'implementazione viene monitorato in status.md.
Le linee guida proposte di seguito hanno lo scopo di garantire l'implementazione di verificatori e funzioni di forma di alta qualità per le operazioni StableHLO.
Proposta
Queste proposte si applicano sia alla revisione delle implementazioni esistenti sia al raggiungimento di nuove operazioni fino a una copertura completa.
(P1) Utilizzare la specifica StableHLO come fonte di riferimento
La spec è la fonte attendibile per tutti i verificatori e le funzioni di forma delle operazioni StableHLO. I verificatori esistenti e le funzioni di forma di ogni operazione devono essere rivisitati per essere pienamente allineati alle specifiche. Tieni presente che il documento della specifica continua a evolversi. Nei casi in cui le specifiche di un'operazione non siano disponibili, è consigliabile utilizzare come fonte attendibile l'implementazione XLA, inclusi xla/service/shape_inference.cc e xla/service/hlo_verifier.cc. L'implementazione XLA non copre il dinamismo illimitato, quindi per un dinamismo illimitato applicheremo il buon senso finché non sarà disponibile il dinamismo RFC.
(P2) Sfrutta al meglio gli ODS
I file ODS (come StablehloOps.td) definiscono le operazioni con trait e tipi per ogni operando/attributo/risultato e provvedono a effettuare le verifiche. Pertanto, NON è necessario alcun codice di verifica nelle verifiche o nelle funzioni di forma per le proprietà già garantite dall'ODS. Rimuovi il codice di verifica se è duplicato con ODS, poiché non verrà mai attivato.
Dobbiamo aggiungere test per i vincoli indicati dall'OTS? Consulta Stabilire linee guida per i test.
(P3) Mantenere il codice di verifica nelle verifiche e nelle funzioni di forma
Entrambi:
- verifiers: implementati da
Op::verify()
e - funzioni di forma: implementate da
InferTypeOpInterface
comeOp::inferReturnTypes()
oOp::inferReturnTypeComponents
potrebbero includere un codice di verifica per controllare operandi/attributi/risultati. Una suddivisione iniziale potrebbe essere la seguente: consenti ai verificatori di controllare gli operandi/gli attributi, quindi lascia che le funzioni di forma calcolino solo i tipi di risultati dedotti e verifichi la compatibilità con i tipi di risultati reali. Tuttavia, in realtà questa suddivisione presenta alcuni problemi:
- La funzione di forma può essere chiamata dalle funzioni
build()
generate automaticamente, senza prima chiamare lo strumento di verifica. Quindi anche i relativi input devono essere verificati nella funzione di forma. - Codice duplicato: ad esempio, negli strumenti di verifica eseguiamo alcune elaborazioni sugli operandi e poi verifichiamo alcuni risultati intermedi. Nelle funzioni di forma, questi risultati intermedi sono utili per dedurre i risultati finali. Questi risultati intermedi devono essere calcolati due volte.
- Oneri di manutenzione: le verifiche di un'operazione si basano su due metodi diversi.
La soluzione è la seguente:
Per la maggior parte delle operazioni senza regioni (come
PadOp
): prova a inserire tutto il codice di verifica nelle funzioni di forma ed elimina completamente gli strumenti di verifica. Nei casi in cui questo non sia possibile a causa dell'impossibilità di dedurre i tipi restituiti (ad esempio conReshapeOp
oBroadcastInDimOp
), crea uno strumento di verifica che contenga la logica di verifica necessaria. Le operazioni deducibili di solito, comeAddOp
, potrebbero comunque aver bisogno di uno strumento di verifica per eseguire ulteriori verifiche, perché verificano i vincoli di un tipo restituito fornito, che non è accessibile nei metodi di inferenza del tipo/della forma.Per le operazioni con regioni (come
ReduceOp/IfOp
; l'elenco completo è qui): i builder generati automaticamente non prendono le regioni come parametri, quindi se questi builder prevedono l'inferenza del tipo, la funzione di forma verrà chiamata con le regioni vuote (vedi questo esempio).Se le regioni non sono necessarie per l'inferenza del tipo (come
ReduceOp
), inserisci la logica di verifica relativa all'area geografica nelle verifiche, anziché nelle funzioni di forma. Duplica un codice se è inevitabile.Se sono necessarie le regioni per l'inferenza del tipo (
IfOp/CaseOp/MapOp
), anche la funzione di forma deve verificare esplicitamente che le regioni non siano vuote, anche se l'ODS potrebbe già garantire la sua esistenza nella definizione dell'operatore.
(P4) Stabilire le linee guida per i test
Dobbiamo aggiungere/gestire test per le verifiche coperte da ODS?
Non lo facciamo. I test dovrebbero concentrarsi sulle verifiche e sulle funzioni di forma, mentre le modifiche agli ODS richiedono una revisione di questa operazione.
Ma fai attenzione ai pezzi mancanti: ad esempio, se l'operazione contiene il tratto SameOperandsAndResultShape
, che controlla solo le forme ma non il tipo di elemento, la verifica dei tipi di elementi di operandi/risultati richiede comunque dei test.
Dove vengono posizionati i test per i verificatori e per l'inferenza dei tipi?
ops_stablehlo.mlir contiene i casi positivi di operazioni e almeno un test negativo per ogni errore di verifica. È anche in grado di verificare che il tipo restituito dedotto sia compatibile con (non lo stesso) del tipo di risultato reale.
infer_stablehlo.mlir verifica l'esistenza della funzione shape di una riga operativa per riga con hlo_test_infer.get_return_type_components"(%x):...
e verifica che il tipo dedotto corrisponda esattamente a quanto previsto. In generale, un test positivo per operazione.
Cosa fare
Quando implementi o rivisiti la funzione di verifica e/o la funzione di forma di un'operazione:
Inserisci tutti i casi positivi e negativi in ops_stablehlo.mlir.
Aggiungi un singolo test positivo in infer_stablehlo.mlir per testare l'interfaccia.
(Facoltativo) Se un'operazione è complicata e potrebbe contenere molti test, valuta la possibilità di aggiungere un file di test separato denominato
verify_<op_name>.mlir
overify_<your_topic>.mlir
nella stessa cartella.