-chlo-legalize-to-stablehlo
Legalizuje operacje z CHLO na operacje StableHLO i Shape
-shape-legalize-to-stablehlo
Legalizacja operacji związanych z kształtem na potrzeby StableHLO.
Eksperymentalne przekształcenie, które legalizuje operacje związane z kształtem na operacje StableHLO.
Połączenie obliczeń kształtu i danych za pomocą opcjonalnego przekazywania umożliwi ekosystemowi StableHLO potencjalne wykorzystanie potoków kompilacji, które używają operacji StableHLO do modelowania dynamizmu.
-stablehlo-canonicalize-dynamism
Przekształca dynamiczne operacje StableHLO w statyczne.
Zastępuje dynamiczne operacje StableHLO, takie jak DynamicReshapeOp, odpowiednimi statycznymi odpowiednikami, np. DynamicReshapeOp na ReshapeOp lub DynamicBroadcastInDim na BroadcastInDim, jeśli wszystkie dynamiczne elementy tych operacji są w rzeczywistości stałymi.
%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.
Sprawdzanie niestandardowych wywołań funkcji shape_assertion.
Asercje kształtu weryfikują ograniczenia dotyczące wymiarów dynamicznych w StableHLO.
Jeśli na przykład platforma musi wymusić ograniczenie DimA < 2, może wygenerować ten kod 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" }
Jeśli kształty są prawidłowe, po przejściu testu stablehlo.custom_callzostanie usunięty.
Opcje
-enable-shape-assertions : Whether shape assertions may generate errors.
-stablehlo-compatibility-expander
Rozszerzenie zgodności dla operacji StableHLO.
W najnowszych wersjach operacje StableHLO są aktualizowane lub wprowadzane są nowe operacje. Ten opcjonalny pakiet rozszerza zgodność wsteczną ze starszymi wersjami StableHLO, rozkładając nowsze operacje StableHLO na równoważne operacje obsługiwane przez te starsze wersje.
Dlaczego jest to karta dla zainteresowanych?
Czasami ulepszenia operacji StableHLO są używane, aby znacznie uprościć obsługę niektórych typowych wzorców w ekosystemie OpenXLA. Obejmuje to m.in. TanOp, który ma szerokie wsparcie w zakresie frameworków i kompilatorów, a także wymiary przetwarzania wsadowego gather/scatter, które można reprezentować za pomocą wycinków, ale znacznie utrudnia to dzielenie na fragmenty. W przypadku tej kategorii nowych funkcji nie oferujemy automatycznego obniżania wersji, ponieważ może to spowodować utratę ważnych informacji używanych w kolejnych optymalizacjach. Ten etap może służyć do rozszerzania tych operacji na podstawie wersji docelowej, aby zmaksymalizować zgodność kosztem potencjalnie mniej optymalnej kompilacji.
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>
}
Opcje
-target : The target version. Must be a version of the form #.#.#.
-stablehlo-complex-math-expander
Rozszerzenie do złożonych operacji matematycznych w StableHLO.
Złożone operacje matematyczne StableHLO są rozkładane na operacje matematyczne na liczbach rzeczywistych StableHLO.
To stwierdzenie opiera się na założeniu, że nie istnieje sprzęt, który natywnie obsługuje liczby zespolone ani złożone operacje matematyczne. Oznacza to, że mechanizmy rezerwowe w przypadku złożonych operacji matematycznych, które mogą być implementowane przez kompilatory, są zbędne. Włączenie tego przekształcenia spowoduje rozwinięcie wszystkich złożonych operacji matematycznych StableHLO.
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
Przekaż, aby przekształcić IR na liczby całkowite bez znaku.
-stablehlo-legalize-composite-to-call
Zastępuje operacje złożone wywołaniem ich dekompozycji.
Zastępuje operacje złożone wywołaniem ich dekompozycji, np. w tym przypadku:
stablehlo.composite "my_namespace.my_op" %arg0, %arg1 {
decomposition = @bar,
version = 1,
composite_attributes = {
"my_attribute": "my_value"
}
}
Zmieni się w:
func.call @bar(%arg0, %arg1)
Podzbiór kompozytów można wykluczyć z tej transformacji za pomocą flagi „except”, np.:
stablehlo-opt --stablehlo-legalize-composite-to-call=except='foo.baz,foo.qux'
Opcje
-except : Names of composites that should not be replaced with calls.
-stablehlo-legalize-deprecated-ops
Zastąp wycofane operacje dobrze obsługiwanymi operacjami.
W RFC dotyczącym wycofania zestawu operacji StableHLO w wersji 1.0 (#2283) proponujemy usunięcie kilku zbędnych operacji. Ten etap pomaga ocenić wpływ tych usunięć w różnych potokach kompilacji, legalizując je w odniesieniu do ich długoterminowych odpowiedników.
Opcje
-fail-on-unused : Fail on (mostly) unused ops that are deprecated without any fallback.
-stablehlo-legalize-qdq-to-quantized-op
Scalanie wzorca (dekwantyzacja, operacja zmiennoprzecinkowa i kwantyzacja) z kwantyzowaną operacją StableHLO
Scalanie wzorca (dekwantyzacja, operacja zmiennoprzecinkowa i kwantyzacja) z operacją skwantyzowaną StableHLO Uwaga: ten etap nie usuwa żadnych istniejących operacji. Na przykład ten program:
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>>
}
Zmieni się w:
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
Konwertowanie skwantowanych operacji StableHLO na podstawowe operacje matematyczne StableHLO.
Konwertowanie programów StableHLO, które używają typów UniformQuantized, na semantycznie równoważne operacje matematyczne na liczbach całkowitych.
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>>
}
Zmieni się w:
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
Rozkładanie skwantyzowanej operacji StableHLO na wzorzec (dekwantyzacja, operacja zmiennoprzecinkowa i kwantyzacja).
Rozkładanie skwantyzowanych programów StableHLO za pomocą operacji kwantyzacji i odkwantyzacji o jednolitym zakresie. Na przykład ten program
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>>
}
Zmieni się w:
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
Legalize StableHLO to VHLO.
Przekształć StableHLO do najnowszej wersji operacji w VHLO. Te operacje można następnie obniżyć do starszych wersji VHLO w celu zapewnienia zgodności z przyszłymi wersjami za pomocą VhloToVersionPass.
stablehlo.exponential %[[ARG0]] <{result_accuracy = DEFAULT}> : tensor<f32>
# ====>
"vhlo.exponential_v2"(%[[ARG0]]) <{result_accuracy = #vhlo.DEFAULT_v1}> : !vhlo.tensor_v1<!vhlo.f32_v1>
Więcej informacji o tym, jak VHLO jest używany do zachowania zgodności z wersjami wcześniejszymi i późniejszymi, znajdziesz w vhlo.md > The VHLO dialect.
Opcje
-allow-other-dialects : Allow serialization to use other (potentially unstable) dialects, inserts unrealized casts between dialects.
-stablehlo-refine-arguments
Uściśla kształty argumentów funkcji głównej.
Modyfikuje argumenty funkcji głównej za pomocą sygnatury typu wejściowego.
Zawiera argumenty w custom_call @stablehlo.shape_refinement_operand_wrapper
, aby zachować ważność IR przed uruchomieniem ulepszania kształtu.
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>
...
}
Za pomocą znaku refinedTypesOption możesz określić listę typów doprecyzowanych.
Można to określić w MLIR za pomocą --types='tensor<...>,tensor<...>' lub przekazać do metody tworzenia karty. Lista typów udoskonalenia musi określać typ każdego argumentu udoskonalanej metody main.
Opcje
-types : The new types to be used for the main function's arguments, specified as an MLIR TypeRange 'tensor<1x2xf32>, ...'
-stablehlo-refine-shapes
Uściśla kształty w programie StableHLO.
Przechodzi przez program StableHLO, dopracowując kształty w operacjach.
Głównym zastosowaniem tego przekazywania jest specjalizowanie programów o dynamicznie kształtowanych kształtach w statyczne kształty. Jeśli program StableHLO o dynamicznych kształtach ma odpowiednią strukturę, zaktualizowanie typów argumentów z dynamicznych na statyczne i uruchomienie tego przekształcenia spowoduje propagowanie statycznych kształtów w całym programie.
Ten etap usuwa custom_call @shape_refinement_operand_wrapper, zastępując użycie wyniku bezpośrednio operandem i propagując statyczne kształty w całym programie.
%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>
Moduły, które można wykorzystać do dopracowywania kształtu, muszą mieć te właściwości:
- Wszystkie kształty dynamiczne zależą tylko od kształtów wejściowych (nie ma zależności kształtu od zawartości tablicy wejściowej). Operacje, które zależą przechodnio tylko od kształtów wejściowych (np. podanych przez
stablehlo.get_dimension_size) lub stałych globalnych, takich jak rozwiązane wartości liczb całkowitych symbolicznych (np. tensor: A = 5), nazywamy operacjami dimension. Wszystkie wartości wymiarów można przekształcić w stałe za pomocą międzyproceduralnego składania stałych. - Funkcje pośrednie mogą przyjmować na początku listy argumentów kilka argumentów tokenowych (typu !stablehlo.token), a następnie kilka globalnych argumentów stałych, które są stałymi skalarami całkowitymi, np. rozwiązane wartości liczb symbolicznych (czyli tensor
: A = 5). - Niektóre funkcje pośrednie mogą zwracać obliczenia na stałych globalnych, np.
floordivna wartościach symint. Funkcje te zwracają tylko stałe wartości po dopracowaniu. Te funkcje są wstawiane w kodzie. - Wszystkie wywołania pojedynczej funkcji są rozwiązywane w ten sam sposób, a wywołania funkcji rekurencyjnych lub współrekurencyjnych nie są wykonywane.
-stablehlo-wrap-in-composite
Zawiera niekompozytowe działanie StableHLO w działaniu kompozytowym.
Zawiera operacje StableHLO w operacjach stablehlo.composite.
Weźmy na przykład prosty program StableHLO:
func.func @main(%arg0 : tensor<2xf32>, %arg1 : tensor<2xf32>) -> tensor<2xf32> {
%0 = stablehlo.add %arg0, %arg1 : tensor<2xf32>
return %0 : tensor<2xf32>
}
Zastosowanie tego przekształcenia do operacji zawijania stablehlo.add spowoduje powstanie tego programu:
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>
}
Uwagi:
- Atrybut
namewygenerowanej operacjistablehlo.compositebędzie zawsze taki sam jak nazwa oryginalnej operacji, która została opakowana (np. jeśli opakujesz operacjęstablehlo.add, operacja złożona również będzie się nazywać"stablehlo.add"). - Funkcja prywatna, która zawiera oryginalną operację (do której odwołuje się atrybut
decompositionoperacjistablehlo.composite), będzie miała nazwę zgodną ze wzorcem<op_name>.impl[.N], gdzie<op_name>to nazwa oryginalnej operacji, aNto unikalny identyfikator liczbowy wygenerowany w celu uniknięcia konfliktów nazw w module.
Karty można używać na 2 sposoby:
Tryb 1. Używanie wiersza poleceń
Ten tryb jest przeznaczony do debugowania lub testowania, ponieważ zapewnia minimalną kontrolę nad atrybutami wygenerowanych operacji stablehlo.composite.
Obejmuje wszystkie wystąpienia operacji określonych za pomocą opcji op-names (lista nazw operacji rozdzielona przecinkami). Atrybuty nowo utworzonej operacji stablehlo.composite będą takie same jak atrybuty pierwotnej operacji.
Przykład użycia:
stablehlo-opt input.mlir --stablehlo-wrap-in-composite=op-names='stablehlo.add,stablehlo.mul' -o output.mlir
Tryb 2. Automatyczne opakowywanie modułu z niestandardową obsługą atrybutów
Ten tryb rozszerza zautomatyzowane opakowywanie na cały moduł, zapewniając precyzyjną kontrolę nad tym, które operacje są opakowywane, i ich atrybutami.
Można to zrobić za pomocą interfejsu createStablehloWrapInCompositePass API, który przyjmuje jako argument CompositeAttributeProviderMap.
CompositeAttributeProviderMap to mapa, która określa, które operacje należy uwzględnić w przypadku opakowywania i jak należy obsługiwać ich atrybuty. Jego semantyka jest następująca:
- Klucze (mlir::TypeID):
TypeIDoperacji MLIR. Jeśli operacjaTypeIDpasuje do klucza na mapie, staje się kandydatem do opakowania. - Wartości (funkcje Lambda): funkcja Lambda typu
std::function<std::optional<NamedAttrList>(Operation*)>. Ta funkcja jest stosowana do każdej operacji kandydującej.- Dane wejściowe:
mlir::Operation*, czyli instancja typu operacji odpowiadająca klawiszowiTypeID. - Zwracana wartość:
std::optional<NamedAttrList>.- Jeśli funkcja lambda zwraca wartość
NamedAttrList(zawartą wstd::optional), operacja jest zawijana w operacjęstablehlo::composite, a zwrócone atrybuty są używane do ustawiania atrybutów kompozycji. - Jeśli funkcja lambda zwróci wartość
std::nullopt, operacja nie zostanie opakowana. Umożliwia to selektywne zawijanie na podstawie niestandardowych kryteriów.
- Jeśli funkcja lambda zwraca wartość
- Dane wejściowe:
Przykład (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;
}
Opcje
-op-names : The names of the ops to wrap.
-version : The version number of the composite op.
-vhlo-legalize-to-stablehlo
Legalizacja VHLO do StableHLO.
-vhlo-to-version
Konwertuj między wersjami VHLO, aby zapewnić zgodność.
Konwertuje wersje VHLO na potrzeby uaktualniania i obniżania wersji IR, aby zachować zgodność z wersjami wcześniejszymi i późniejszymi.
"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}>
Więcej informacji o tym, jak VHLO jest używany do zachowania zgodności z wersjami wcześniejszymi i późniejszymi, znajdziesz w vhlo.md > The VHLO dialect.
Opcje
-target : The target version. Must be a version of the form #.#.# .