-chlo-legalize-to-stablehlo
Legalisiert den CHLO-Vorgangsablauf zu StableHLO- und Shape-Vorgängen
-shape-legalize-to-stablehlo
Formbezogene Vorgänge in StableHLO legalisieren:
Ein experimenteller Pass, der formbezogene Vorgänge in StableHLO-Vorgänge umwandelt.
Durch die Zusammenführung von Form- und Datenberechnungen über einen optionalen Durchlauf kann das StableHLO-Ökosystem möglicherweise die Kompilierungspipelines nutzen, die StableHLO-Vorgänge verwenden, um Dynamik zu modellieren.
-stablehlo-canonicalize-dynamism
Kanonisiert dynamische StableHLO-Vorgänge in statische Vorgänge.
Ersetzt dynamische StableHLO-Vorgänge wie DynamicReshapeOp durch die entsprechenden statischen Gegenstücke wie DynamicReshapeOp bis ReshapeOp oder DynamicBroadcastInDim bis BroadcastInDim, wenn alle dynamischen Elemente dieser Vorgänge tatsächlich Konstanten sind.
%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.
Benutzerdefinierte shape_assertion-Aufrufe validieren.
Mit Shape-Assertions werden Einschränkungen für dynamische Dimensionen in StableHLO validiert.
Wenn beispielsweise ein Framework eine Einschränkung von DimA < 2 erzwingen muss, kann der folgende IR ausgegeben werden:
%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" }
Wenn die Formen nach dem Durchgang korrekt sind, wird stablehlo.custom_call entfernt.
Optionen
-enable-shape-assertions : Whether shape assertions may generate errors.
-stablehlo-compatibility-expander
Kompatibilitätserweiterung für StableHLO-Vorgänge:
StableHLO-Vorgänge werden in den neuesten Versionen aktualisiert oder es werden neue Vorgänge eingeführt. Dieser Opt-in-Pass erweitert die Abwärtskompatibilität mit älteren StableHLO-Versionen, indem neuere StableHLO-Vorgänge in entsprechende Vorgänge zerlegt werden, die von diesen älteren Versionen unterstützt werden.
Warum ist das ein Opt-in-Pass?
Gelegentlich werden StableHLO-Optimierungen verwendet, um die Verarbeitung bestimmter gängiger Muster im OpenXLA-Ökosystem erheblich zu vereinfachen. Dazu gehören beispielsweise TanOp, das eine hohe Framework- und Compilerunterstützung bietet, sowie Batching-Dimensionen für Gather/Scatter, die mit Slices dargestellt werden können, das Sharding jedoch erheblich erschweren. Für diese Kategorie neuer Funktionen bieten wir kein automatisches Downgrade an, da dadurch wichtige Informationen verloren gehen können, die für nachfolgende Optimierungen verwendet werden. Mit diesem Durchlauf können diese Vorgänge basierend auf einer Zielversion erweitert werden, um die Kompatibilität zu maximieren. Das kann jedoch zu einer weniger optimalen Kompilierung führen.
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>
}
Optionen
-target : The target version. Must be a version of the form #.#.#.
-stablehlo-complex-math-expander
Expander für komplexe mathematische StableHLO-Operationen.
Komplexe mathematische Operationen in StableHLO sind Zerlegungen, bei denen reelle mathematische Operationen in StableHLO verwendet werden.
Diese Aussage basiert auf der Annahme, dass es keine Hardware gibt, die komplexe Zahlen oder komplexe mathematische Operationen nativ unterstützt. Das bedeutet, dass die Fallback-Mechanismen für komplexe mathematische Operationen, die Compiler möglicherweise implementieren, überflüssig sind. Wenn Sie diesen Durchgang aktivieren, werden alle komplexen mathematischen StableHLO-Operationen erweitert.
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
Pass, um die Zwischenrepräsentation in vorzeichenlose Ganzzahlen zu transformieren.
-stablehlo-legalize-composite-to-call
Ersetzt zusammengesetzte Vorgänge durch einen Aufruf ihrer Zerlegung.
Ersetzt zusammengesetzte Vorgänge durch einen Aufruf ihrer Zerlegung, z.B. wie unten:
stablehlo.composite "my_namespace.my_op" %arg0, %arg1 {
decomposition = @bar,
version = 1,
composite_attributes = {
"my_attribute": "my_value"
}
}
wird zu:
func.call @bar(%arg0, %arg1)
Eine Teilmenge von Composites kann mit dem Flag „except“ von dieser Transformation ausgenommen werden, z.B.:
stablehlo-opt --stablehlo-legalize-composite-to-call=except='foo.baz,foo.qux'
Optionen
-except : Names of composites that should not be replaced with calls.
-stablehlo-legalize-deprecated-ops
Veraltete Vorgänge in gut unterstützte Vorgänge umwandeln:
Im RFC #2283 zum Entfernen von StableHLO v1.0-Opset wird vorgeschlagen, mehrere redundante Vorgänge zu entfernen. Mit diesem Durchlauf lässt sich die Auswirkung dieser Entfernungen in verschiedenen Kompilierungspipelines bewerten, indem sie in ihre langfristig unterstützten Gegenstücke umgewandelt werden.
Optionen
-fail-on-unused : Fail on (mostly) unused ops that are deprecated without any fallback.
-stablehlo-legalize-qdq-to-quantized-op
Fuse-Muster (Dequantisierung, Gleitkommaoperation und Quantisierung) in quantisierten StableHLO-Vorgang
Das Muster „Fuse“ (Dequantisieren, Gleitkommaoperation und Quantisieren) in StableHLO-quantisierte Operation einfügen Hinweis: Bei diesem Durchlauf werden keine vorhandenen Operationen gelöscht. Beispiel: Das folgende Programm
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>>
}
wird zu:
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
Von StableHLO-quantisierten Operationen zu mathematischen StableHLO-Primitive-Operationen konvertieren:
Konvertieren Sie StableHLO-Programme mit UniformQuantized-Typen in semantisch äquivalente Ganzzahl-Mathematikoperationen.
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>>
}
wird zu:
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
Quantisierten StableHLO-Vorgang in das Muster „Dequantisieren, Gleitkomma-Vorgang und Quantisieren“ zerlegen.
Zerlegen Sie quantisierte StableHLO-Programme mit einheitlichen Quantisierungs-/Dequantisierungsvorgängen. Beispiel:
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>>
}
wird zu:
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
StableHLO in VHLO legalisieren:
Legalize StableHLO to the latest version of ops in VHLO. Diese Vorgänge können dann mit VhloToVersionPass zur Vorwärtskompatibilität auf ältere Versionen von VHLO herabgestuft werden.
stablehlo.exponential %[[ARG0]] <{result_accuracy = DEFAULT}> : tensor<f32>
# ====>
"vhlo.exponential_v2"(%[[ARG0]]) <{result_accuracy = #vhlo.DEFAULT_v1}> : !vhlo.tensor_v1<!vhlo.f32_v1>
vhlo.md > The VHLO dialect enthält ausführliche Informationen dazu, wie VHLO verwendet wird, um die Vorwärts- und Rückwärtskompatibilität zu gewährleisten.
Optionen
-allow-other-dialects : Allow serialization to use other (potentially unstable) dialects, inserts unrealized casts between dialects.
-stablehlo-refine-arguments
Verfeinert die Argumentformen der Hauptfunktion.
Ändert die Argumente der Hauptfunktion mithilfe der Eingabetyp-Signatur.
Umschließt Argumente in custom_call @stablehlo.shape_refinement_operand_wrapper, damit das IR gültig bleibt, bevor die Formoptimierung ausgeführt wird.
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>
...
}
Mit refinedTypesOption kann eine Liste mit verfeinerten Typen angegeben werden.
Dies kann in MLIR mit --types='tensor<...>,tensor<...>' angegeben oder an die Methode zum Erstellen von Karten/Tickets übergeben werden. In der Liste der Verfeinerungstypen muss der Typ jedes Arguments für die zu verfeinernde main-Methode angegeben werden.
Optionen
-types : The new types to be used for the main function's arguments, specified as an MLIR TypeRange 'tensor<1x2xf32>, ...'
-stablehlo-refine-shapes
Optimiert Formen in einem StableHLO-Programm.
Hier wird ein StableHLO-Programm beschrieben, in dem Formen in Vorgängen verfeinert werden.
Der wichtigste Anwendungsfall für diesen Pass ist die Spezialisierung von dynamisch geformten Programmen auf statische Formen. Wenn ein dynamisch geformtes StableHLO-Programm die richtige Struktur hat, werden durch das Aktualisieren der Argumenttypen von dynamischen zu statischen Formen und das Ausführen dieses Durchgangs statische Formen im gesamten Programm weitergegeben.
Bei diesem Durchlauf werden custom_call @shape_refinement_operand_wrapper entfernt, indem Verwendungen des Ergebnisses direkt durch den Operanden ersetzt werden. Außerdem werden statische Formen im gesamten Programm weitergegeben.
%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>
Module, die für die Formoptimierung infrage kommen, müssen die folgenden Eigenschaften haben:
- Alle dynamischen Formen hängen nur von den Eingabeformen ab (keine Formabhängigkeit vom Inhalt des Eingabearrays). Vorgänge, die transitiv nur von den Eingabeformen (z.B. wie durch
stablehlo.get_dimension_sizeangegeben) oder globalen Konstanten wie den aufgelösten Werten symbolischer Ganzzahlen (d.h. Tensor: A = 5) abhängen, werden als dimension-Vorgänge bezeichnet. Alle Dimensionswerte können durch interprozedurale Konstantenfaltung in Konstanten aufgelöst werden. - Zwischenfunktionen können am Anfang der Argumentliste eine Reihe von Token-Argumenten (vom Typ !stablehlo.token) und danach einige globale konstante Argumente annehmen, die konstante Ganzzahlskalare sind, z. B. die aufgelösten Werte symbolischer Ganzzahlen (d. h. Tensor
: A = 5 ). - Einige Zwischenfunktionen geben möglicherweise Berechnungen für globale Konstanten zurück, z.B.
floordivfür symint-Werte. Diese Funktionen geben nach der Optimierung nur konstante Werte zurück. Diese Funktionen werden inline eingefügt. - Alle Aufrufe einer einzelnen Funktion werden in dieselben Argumentformen aufgelöst und es werden keine rekursiven / korekursiven Funktionsaufrufe ausgeführt.
-stablehlo-wrap-in-composite
Umschließt einen nicht zusammengesetzten StableHLO-Vorgang in einem zusammengesetzten Vorgang.
Umschließt StableHLO-Vorgänge in stablehlo.composite-Vorgängen.
Betrachten wir beispielsweise ein einfaches StableHLO-Programm:
func.func @main(%arg0 : tensor<2xf32>, %arg1 : tensor<2xf32>) -> tensor<2xf32> {
%0 = stablehlo.add %arg0, %arg1 : tensor<2xf32>
return %0 : tensor<2xf32>
}
Wenn Sie diesen Durchgang auf die Wrap-Vorgänge stablehlo.add anwenden, erhalten Sie das folgende Programm:
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>
}
Hinweise:
- Das Attribut
namedes generiertenstablehlo.composite-Vorgangs ist immer dasselbe wie der Name des ursprünglichen Vorgangs, der umschlossen wurde. Wenn Sie beispielsweise einenstablehlo.add-Vorgang umschließen, wird der zusammengesetzte Vorgang auch"stablehlo.add"genannt. - Die private Funktion, die den ursprünglichen Vorgang kapselt (auf den mit dem Attribut
decompositiondes Vorgangsstablehlo.compositeverwiesen wird), wird nach dem Muster<op_name>.impl[.N]benannt, wobei<op_name>der Name des ursprünglichen Vorgangs undNeine eindeutige Ganzzahl-ID ist, die generiert wird, um Namenskonflikte innerhalb des Moduls zu vermeiden.
Diese Karte kann auf zwei Arten verwendet werden:
Modus 1: Befehlszeilenverwendung
Dieser Modus ist für das Debugging oder Testen vorgesehen, da er nur minimale Kontrolle über die Attribute der generierten stablehlo.composite-Vorgänge bietet.
Es umschließt alle Instanzen von Vorgängen, die mit den Optionen op-names (eine durch Kommas getrennte Liste von Vorgangsnamen) angegeben werden. Die Attribute des neu erstellten stablehlo.composite-Vorgangs sind dieselben wie die Attribute des ursprünglichen Vorgangs.
Verwendungsbeispiel:
stablehlo-opt input.mlir --stablehlo-wrap-in-composite=op-names='stablehlo.add,stablehlo.mul' -o output.mlir
Modus 2: Programmatisches Wrapping auf Modulebene mit benutzerdefinierter Attributverarbeitung
In diesem Modus wird das programmatische Wrapping auf das gesamte Modul ausgeweitet. So haben Sie die Möglichkeit, genau festzulegen, welche Vorgänge und Attribute umbrochen werden.
Dies wird durch die Verwendung der createStablehloWrapInCompositePass API erreicht, die ein CompositeAttributeProviderMap als Argument akzeptiert.
CompositeAttributeProviderMap ist eine Map, die festlegt, welche Vorgänge für das Wrapping infrage kommen und wie ihre Attribute verarbeitet werden sollen. Die Semantik ist wie folgt:
- Schlüssel (mlir::TypeID):
TypeIDeines MLIR-Vorgangs. Wenn derTypeID-Wert eines Vorgangs mit einem Schlüssel in der Zuordnung übereinstimmt, kommt er für das Wrapping infrage. - Werte (Lambda-Funktionen): Lambda-Funktion vom Typ
std::function<std::optional<NamedAttrList>(Operation*)>. Diese Funktion wird auf jeden Kandidatenvorgang angewendet.- Eingabe:Ein
mlir::Operation*, das eine Instanz des Vorgangstyps ist, der demTypeID-Schlüssel entspricht. - Rückgabewert:Eine
std::optional<NamedAttrList>.- Wenn die Lambda-Funktion
NamedAttrList(eingeschlossen instd::optional) zurückgibt, wird der Vorgang in einenstablehlo::composite-Vorgang eingeschlossen und die zurückgegebenen Attribute werden verwendet, um die Attribute des Composites festzulegen. - Wenn die Lambda-Funktion
std::nulloptzurückgibt, wird der Vorgang nicht umschlossen. So können Sie das Wrapping basierend auf benutzerdefinierten Kriterien selektiv vornehmen.
- Wenn die Lambda-Funktion
- Eingabe:Ein
Beispiel (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;
}
Optionen
-op-names : The names of the ops to wrap.
-version : The version number of the composite op.
-vhlo-legalize-to-stablehlo
VHLO in StableHLO umwandeln:
-vhlo-to-version
Zwischen VHLO-Versionen konvertieren, um die Kompatibilität zu gewährleisten
Konvertiert zwischen Versionen von VHLO für das Upgrading und Downgrading von IR, um die Vorwärts- und Rückwärtskompatibilität zu erhalten.
"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}>
vhlo.md > The VHLO dialect enthält ausführliche Informationen dazu, wie VHLO verwendet wird, um die Vorwärts- und Rückwärtskompatibilität zu gewährleisten.
Optionen
-target : The target version. Must be a version of the form #.#.# .