Jelajahi Sumber

Add MLIR support (#1044)

Lutz Roeder 2 bulan lalu
induk
melakukan
f6607858a6
4 mengubah file dengan 437 tambahan dan 178 penghapusan
  1. 157 9
      source/mlir-metadata.json
  2. 262 167
      source/mlir.js
  3. 1 1
      tools/mlir
  4. 17 1
      tools/mlir-script.js

+ 157 - 9
source/mlir-metadata.json

@@ -1774,6 +1774,35 @@
     ],
     "assemblyFormat": "$global `[` $global_indices `]` `,` $lds `[` $lds_indices `]` attr-dict `:` type($global) `,` type($lds) `->` type(results)"
   },
+  {
+    "name": "amdgpu.make_gather_dma_descriptor",
+    "summary": "Make all descriptor groups needed by TensorLoadToLDS/TensorStoreFromLDS.",
+    "inputs": [
+      { "name": "base", "type": "AMDGPU_TDMGatherBaseType" },
+      { "name": "indices", "type": "AnyTypeOf<[VectorOfMinMaxLengthAndType<1, 8, [I32]>, VectorOfMinMaxLengthAndType<1, 16, [I16]>]>" },
+      { "name": "global_dynamic_sizes", "type": "Variadic<Index>" },
+      { "name": "global_dynamic_strides", "type": "Variadic<Index>" },
+      { "name": "shared_dynamic_sizes", "type": "Variadic<Index>" },
+      { "name": "workgroup_mask", "type": "Optional<AMDGPU_ConcreteVector<I1, 16>>" },
+      { "name": "early_timeout", "type": "Optional<I1>" },
+      { "name": "pad_amount", "type": "Optional<I32>" },
+      { "name": "pad_interval", "type": "Optional<I32>" },
+      { "name": "atomic_barrier_address", "type": "Optional<AnyMemRef>" },
+      { "name": "atomic_barrier_indices", "type": "Variadic<Index>" },
+      { "name": "global_increment", "type": "Optional<Index>" },
+      { "name": "lds_increment", "type": "Optional<I32>" },
+      { "name": "iteration_count", "type": "Optional<Index>" }
+    ],
+    "outputs": [
+      { "name": "desc", "type": "AMDGPU_TDMDescriptorType" }
+    ],
+    "attributes": [
+      { "name": "global_static_sizes", "type": "DenseI64ArrayAttr" },
+      { "name": "global_static_strides", "type": "DenseI64ArrayAttr" },
+      { "name": "shared_static_sizes", "type": "DenseI64ArrayAttr" }
+    ],
+    "assemblyFormat": "$base `[` $indices `]`\n    `globalSize` custom<DynamicIndexList>($global_dynamic_sizes, $global_static_sizes)\n    `globalStride` custom<DynamicIndexList>($global_dynamic_strides, $global_static_strides)\n    `sharedSize` custom<DynamicIndexList>($shared_dynamic_sizes, $shared_static_sizes)\n    ( `padShared` `(` $pad_amount^ `every` $pad_interval `)` )?\n    ( `workgroupMask` $workgroup_mask^ ( `earlyTimeout` $early_timeout^)?)?\n    ( `atomicBarrier` `(` $atomic_barrier_address^ `[` $atomic_barrier_indices `]`\n                      `:` type($atomic_barrier_address) `)`)?\n    ( `iterate` $global_increment^ `,` $lds_increment `,` $iteration_count )?\n    attr-dict `:` qualified(type($base)) `,` type($indices) `->` type(results)"
+  },
   {
     "name": "amdgpu.memory_counter_wait",
     "summary": "Wait for specified hardware counters",
@@ -5209,7 +5238,89 @@
     "assemblyFormat": "$flag `:` type($flag) `,` `[` `\\n`\n      custom<SwitchOpCases>(ref(type($flag)),$defaultDestination,\n                            $defaultOperands,\n                            type($defaultOperands),\n                            $case_values,\n                            $caseDestinations,\n                            $caseOperands,\n                            type($caseOperands))\n   `]`\n    attr-dict"
   },
   {
-    "name": "check.expect_almost_eq",
+    "name": "check.<iree>.expect_all_true",
+    "summary": "Checks that the operand contains only values that are true.",
+    "description": "Verifies that the operand contains true values, which are represented by any\n    non-zero integer.\n\n    Issues a non-fatal failure if the verification fails.\n\n    ```mlir\n    check.expect_all_true<%device>(%arg0) : !hal.buffer_view\n    check.expect_all_true(%arg1) : tensor<2x2xi32>\n    ```",
+    "inputs": [
+      { "name": "device", "type": "Optional<HAL_Device>" },
+      { "name": "operand", "type": "AnyTypeOf<[HAL_BufferView, TensorOf<[AnySignlessInteger]>]>" }
+    ],
+    "assemblyFormat": "(`` `<` $device^ `>`)?\n    `` `(` $operand `)` attr-dict `:` type($operand)"
+  },
+  {
+    "name": "check.<iree>.expect_almost_eq",
+    "summary": "Checks that the operands are almost equal.",
+    "description": "Verifies that the buffer view or tensor operands with float elements satisfy\n    the Numpy-style fuzzy-comparision condition with parameters `atol`,\n    `rtol`, defined exactly as in NumPy isclose():\n    https://github.com/numpy/numpy/blob/7297f3117d84745bfade1e2f9aec3531e5917500/numpy/_core/numeric.py#L2447-L2449\n\n    The condition being verified on each lhs and rhs value is:\n      lhs == rhs || (isfinite(rhs) && abs(lhs - rhs) <= atol + rtol * abs(rhs)).\n    Note that the `lhs == rhs` part is needed for the case (lhs=+inf, rhs+inf)\n    to return true. Indeed, in that case, lhs-rhs is NaN.\n\n    Issues a non-fatal failure if the verification fails.\n\n    The `atol`, `rtol` parameters may be omitted, in which case some default\n    value is used. The default `atol` is nonzero, while the default `rtol` is\n    zero, which makes these comparision behave closer to exact comparisons as\n    the values being compared get large.\n\n    This default behavior is supported for legacy compatibility and to support\n    some use cases that legitimately don't care, but the majority of use cases\n    should care and so should provide explicit `atol`, `rtol` values.\n\n    ```mlir\n    check.expect_almost_eq(%arg0, %arg1, atol 1.0e-2, rtol 1.0e-3) : tensor<5xf32>\n    ```",
+    "inputs": [
+      { "name": "device", "type": "Optional<HAL_Device>" },
+      { "name": "lhs", "type": "AnyTypeOf<[HAL_BufferView, TensorOf<[AnyFloat]>]>" },
+      { "name": "rhs", "type": "AnyTypeOf<[HAL_BufferView, TensorOf<[AnyFloat]>]>" }
+    ],
+    "attributes": [
+      { "name": "atol", "type": "DefaultValuedAttr<F32Attr, 1.e-4f>" },
+      { "name": "rtol", "type": "DefaultValuedAttr<F32Attr, 0.f>" }
+    ],
+    "assemblyFormat": "(`` `<` $device^ `>`)?\n    `` `(` $lhs `,` $rhs (`` `,` `atol` $atol^)? (`` `,` `rtol` $rtol^)? `)`\n    attr-dict `:` type($lhs)"
+  },
+  {
+    "name": "check.<iree>.expect_almost_eq_const",
+    "summary": "Checks that the tensor operand is almost equal to some constant.",
+    "description": "This op is just a convenience wrapper around the expect_almost_eq op.\n\n    Verifies that the buffer view or tensor operands with float elements satisfy\n    the Numpy-style fuzzy-comparision condition with pararameters `atol`,\n    `rtol`. More details in the description of `expect_almost_eq`.\n\n    Issues a non-fatal failure if the verification fails.\n\n    ```mlir\n    check.expect_almost_eq_const(%const0, dense<[0.999999, 2.0]> : tensor<5xf32>, atol 1.0e-2, rtol 1.0e-3) : tensor<5xf32>\n    ```",
+    "inputs": [
+      { "name": "device", "type": "Optional<HAL_Device>" },
+      { "name": "lhs", "type": "TensorOf<[AnyFloat]>" }
+    ],
+    "attributes": [
+      { "name": "value", "type": "ElementsAttr" },
+      { "name": "atol", "type": "DefaultValuedAttr<F32Attr, 1.e-4f>" },
+      { "name": "rtol", "type": "DefaultValuedAttr<F32Attr, 0.f>" }
+    ],
+    "assemblyFormat": "(`` `<` $device^ `>`)?\n    `` `(` $lhs `,` $value (`` `,` `atol` $atol^)? (`` `,` `rtol` $rtol^)? `)`\n    attr-dict `:` type($lhs)"
+  },
+  {
+    "name": "check.<iree>.expect_eq",
+    "summary": "Checks that the tensor or buffer view operands are equal.",
+    "description": "Verifies that the operands are exactly equal.\n\n    Issues a non-fatal failure if the verification fails.\n\n    ```mlir\n    check.expect_eq(%arg0, %arg1) : tensor<5xi32>\n    ```",
+    "inputs": [
+      { "name": "device", "type": "Optional<HAL_Device>" },
+      { "name": "lhs", "type": "AnyTypeOf<[HAL_BufferView, AnyTensor]>" },
+      { "name": "rhs", "type": "AnyTypeOf<[HAL_BufferView, AnyTensor]>" }
+    ],
+    "assemblyFormat": "(`` `<` $device^ `>`)?\n    `` `(` $lhs `,` $rhs `)` attr-dict `:` type($lhs)"
+  },
+  {
+    "name": "check.<iree>.expect_eq_const",
+    "summary": "Checks that the tensor operand is equal to some constant.",
+    "description": "Verifies that the tensor operand is exactly equal to a constant attribute.\n\n    Issues a non-fatal failure if the verification fails.\n\n    This op is just a convenience wrapper around the expect_eq op.\n\n    ```mlir\n    check.expect_eq_const(%arg0, dense<[1, 2]> : tensor<2xi32>) : tensor<2xi32>\n    ```",
+    "inputs": [
+      { "name": "device", "type": "Optional<HAL_Device>" },
+      { "name": "lhs", "type": "AnyTensor" }
+    ],
+    "attributes": [
+      { "name": "value", "type": "ElementsAttr" }
+    ],
+    "assemblyFormat": "(`` `<` $device^ `>`)?\n    `` `(` $lhs `,` $value `)` attr-dict `:` type($lhs)"
+  },
+  {
+    "name": "check.<iree>.expect_false",
+    "summary": "Checks that the operand is false.",
+    "description": "Verifies that the operand contains a false value, which is represented by\n    zero.\n\n    Issues a non-fatal failure if the verification fails.\n\n    ```mlir\n    check.expect_false(%arg0) : i32\n    ```",
+    "inputs": [
+      { "name": "operand", "type": "AnySignlessInteger" }
+    ],
+    "assemblyFormat": "`(` $operand `)` attr-dict `:` type($operand)"
+  },
+  {
+    "name": "check.<iree>.expect_true",
+    "summary": "Checks that the operand is true.",
+    "description": "Verifies that the operand contains a true value, which is represented by\n    any non-zero integer.\n\n    Issues a non-fatal failure if the verification fails.\n\n    ```mlir\n    check.expect_true(%arg0) : i32\n    ```",
+    "inputs": [
+      { "name": "operand", "type": "AnySignlessInteger" }
+    ],
+    "assemblyFormat": "`(` $operand `)` attr-dict `:` type($operand)"
+  },
+  {
+    "name": "check.<stablehlo>.expect_almost_eq",
     "summary": "Checks that the tensor operands are almost equal",
     "description": "Verifies that the tensor operands with floating-point or complex element\n    types are almost equal within an implementation-defined tolerance.\n\n    ```mlir\n    check.expect_almost_eq %arg0, %arg1, tolerance = 0.001 : f64 : tensor<2xf32>\n    ```",
     "inputs": [
@@ -5222,7 +5333,7 @@
     "assemblyFormat": "$lhs `,` $rhs (`,` `tolerance` `=` $tolerance^)? attr-dict `:` type($lhs)"
   },
   {
-    "name": "check.expect_almost_eq_const",
+    "name": "check.<stablehlo>.expect_almost_eq_const",
     "summary": "Checks the tensor operand is almost equal to some constant",
     "description": "Verifies that the tensor operand with floating-point or complex element\n    type is almost equal to the constant attribute within an\n    implementation-defined tolerance.\n\n    ```mlir\n    check.expect_almost_eq_const %arg0, dense<[0.999999, 2.0]> : tensor<2xf32>, tolerance = 0.0001\n    ```",
     "inputs": [
@@ -5235,7 +5346,7 @@
     "assemblyFormat": "$lhs `,` $value (`,` `tolerance` `=` $tolerance^)? attr-dict"
   },
   {
-    "name": "check.expect_close",
+    "name": "check.<stablehlo>.expect_close",
     "summary": "Checks that the tensor operand ULP-distance to expected tensor operand is within specified limits",
     "description": "Verifies that the floating-point tensor operands, actual and\n    expected, are close within user-defined limits:\n\n      min_ulp_difference <= ulp_difference(actual, reference) <= max_ulp_difference\n\n    where ulp_difference returns the number of all possible floating\n    point values that are greater than or equal to min(actual,\n    expected) and smaller than max(actual, expected) provided that\n    both operands are finite.\n\n    When either of operands is non-finite, expect_close returns true\n    when actual and expected are bitwise equal, or when both values\n    are NaNs, otherwise false. Note that quiet NaN and signaling NaN\n    values with different payloads are all considered equivalent.\n\n    ```mlir\n    check.expect_close %arg0, %arg1, max_ulp_difference = 3, min_ulp_difference = 0 : tensor<2xf32>, tensor<2xf32>\n    ```",
     "inputs": [
@@ -5249,7 +5360,7 @@
     "assemblyFormat": "$actual `,` $expected\n      `,` `max_ulp_difference` `=` $max_ulp_difference\n      (`,` `min_ulp_difference` `=` $min_ulp_difference^)?\n      `:` attr-dict type($actual) `,` type($expected)"
   },
   {
-    "name": "check.expect_eq",
+    "name": "check.<stablehlo>.expect_eq",
     "summary": "Checks that the tensor operands are equal",
     "description": "Verifies that the operands are exactly equal.\n\n    ```mlir\n    check.expect_eq %arg0, %arg1 : tensor<2xi32>\n    ```",
     "inputs": [
@@ -5259,7 +5370,7 @@
     "assemblyFormat": "$lhs `,` $rhs attr-dict `:` type($lhs)"
   },
   {
-    "name": "check.expect_eq_const",
+    "name": "check.<stablehlo>.expect_eq_const",
     "summary": "Checks the tensor operand is equal to some constant",
     "description": "Verifies that the tensor operand is exactly equal to a constant attribute.\n\n    ```mlir\n    check.expect_eq_const %arg0, dense<[1, 2]> : tensor<2xi32>\n    ```",
     "inputs": [
@@ -5271,7 +5382,7 @@
     "assemblyFormat": "$lhs `,` $value attr-dict"
   },
   {
-    "name": "check.expect_serialized_eq",
+    "name": "check.<stablehlo>.expect_serialized_eq",
     "summary": "Checks value of serialized tensor value.",
     "description": "Verifies that the value and type of the serialized tensor `probe_id` match\n    the optionally specified input tensor at iteration `iteration`, using\n    previously serialized filepaths in `index.csv`.\n\n    ```mlir\n    check.expect_serialized_eq %arg0,\n      probe_id = \"probe0\",\n      iter = 0 : tensor<2xi32>\n    ```",
     "inputs": [
@@ -35585,6 +35696,9 @@
     ],
     "hasCustomAssemblyFormat": true
   },
+  {
+    "name": "sdfg.call"
+  },
   {
     "name": "sdfg.consume",
     "summary": "Consume scope",
@@ -92400,6 +92514,17 @@
     ],
     "assemblyFormat": "$target attr-dict `:` functional-type(operands, results)"
   },
+  {
+    "name": "transform.iree.emit_remark",
+    "description": "Emits a diagnostic remark with the given message located at payload ops\n    associated with the given handle. This can be used, e.g., for debugging.",
+    "inputs": [
+      { "name": "handle", "type": "TransformHandleTypeInterface" }
+    ],
+    "attributes": [
+      { "name": "message", "type": "StrAttr" }
+    ],
+    "assemblyFormat": "$message `at` $handle attr-dict `:` type($handle)"
+  },
   {
     "name": "transform.iree.flatten_forall_mapping",
     "description": "Flattens the thread mapping of an `scf.forall` op.\n\n    #### Return modes\n    Emits a definite failure if the target is not an scf.forall op or if\n    the mapping type is not `gpu.thread` or `gpu.warp` with linear dims in\n    descending order.",
@@ -92522,7 +92647,19 @@
     "assemblyFormat": "$target\n    `workgroup_dims` `=` $workgroup_dims\n    (`subgroup_size` `=` $subgroup_size^)?\n    (`sync_after_distribution` `=` $sync_after_distribution^)?\n    attr-dict\n    `:` functional-type($target, results)"
   },
   {
-    "name": "transform.iree.match_callback"
+    "name": "transform.iree.match_callback",
+    "description": "Performs payload IR matching using a C++ callback registered beforehand.\n    The callback is identified by name and is passed the current transform\n    state and the list of handle operands, along with information necessary\n    for error propagation. See `register_match_callbacks` for the description\n    of the callback contract.\n\n    If `failure_propagation_mode` is set to `suppress`, any silenceable errors\n    in the callback (typically, \"failure to match\") will be ignored and the\n    resulting handles will be associated with empty lists of payload\n    operations. Otherwise, silenceable failures are propagated.",
+    "inputs": [
+      { "name": "inputs", "type": "Variadic<TransformHandleTypeInterface>" }
+    ],
+    "outputs": [
+      { "name": "outputs", "type": "Variadic<TransformHandleTypeInterface>" }
+    ],
+    "attributes": [
+      { "name": "callback_name", "type": "StrAttr" },
+      { "name": "failure_propagation_mode", "type": "FailurePropagationMode{propagate|suppress}" }
+    ],
+    "assemblyFormat": "`failures` `(` $failure_propagation_mode `)` $callback_name `(` $inputs `)` attr-dict `:` functional-type($inputs, $outputs)"
   },
   {
     "name": "transform.iree.match.attention",
@@ -92763,7 +92900,9 @@
     "assemblyFormat": "$target attr-dict `:` functional-type(operands, results)"
   },
   {
-    "name": "transform.iree.register_match_callbacks"
+    "name": "transform.iree.register_match_callbacks",
+    "description": "Registers named structured op matcher callbacks specific for IREE to use\n    with `transform.iree.match_callback`. This should be called before first\n    `match_callback` may be executed following the transform dialect control\n    flow.\n\n    The callbacks must have a unique name and a signature compatible with\n    `MatchCallbacksRegistry::MatchCallbackFn`, which currently means\n    `DiagnosedSilenceableFailure(MatchCallbackResult &, Location,\n     const TransformState &, ValueRange)`. The callback receives a \"result\",\n     followed by a location at which errors should be reported, a transform\n     state at the moment of the _match_ (not registration) and a list of\n     handle values passed as operands to the `match_callback` operation.\n     It is expected to populate the \"result\" object with lists of payload\n     operations that will be bound to the handles produced by the\n     `match_callback` operation. The callback may fail, at which point\n     it should produce a silenceable error. The callback currently is not\n     allowed to modify the payload IR (though this may be revised in the\n     future for the purpose of communicating the properties of the IR\n     captured by the match). Therefore, it should not have a reason to\n     produce a definite error.",
+    "assemblyFormat": "attr-dict"
   },
   {
     "name": "transform.iree.reorder_transpose",
@@ -92799,7 +92938,16 @@
     "assemblyFormat": "$for_op\n    attr-dict\n    `:` functional-type(operands, results)"
   },
   {
-    "name": "transform.iree.take_first"
+    "name": "transform.iree.take_first",
+    "description": "Given an arbitrary list of handles associated with potentially empty lists\n    of payload operations, produces two new handles:\n\n      - a handle pointing to the same payload operations as the first operand\n        handle with a non-empty list of payload operations;\n      - a handle pointing to the concatenated list of payload operations\n        associated with any other handle.\n\n    Note that this does not perform any deduplication.\n\n    This operation is useful to select a single target after some potentially\n    unsuccessful matches.",
+    "inputs": [
+      { "name": "inputs", "type": "Variadic<TransformHandleTypeInterface>" }
+    ],
+    "outputs": [
+      { "name": "first", "type": "TransformHandleTypeInterface" },
+      { "name": "rest", "type": "TransformHandleTypeInterface" }
+    ],
+    "assemblyFormat": "$inputs attr-dict `:` functional-type($inputs, results)"
   },
   {
     "name": "transform.iree.test_gpu_vector_distribution",

+ 262 - 167
source/mlir.js

@@ -1018,7 +1018,6 @@ mlir.Parser = class {
         this._redirect = new Map([
             ['builtin.func', 'func.func'],
             ['builtin.constant', 'arith.constant'],
-            ['func.constant', 'arith.constant'],
             ['builtin.return', 'func.return'],
             ['builtin.select', 'arith.select'],
             ['scf.select', 'arith.select'],
@@ -2231,7 +2230,7 @@ mlir.Parser = class {
         if (attrT) {
             return attrT(this, type);
         }
-        return this.parseAttribute();
+        return this.parseAttribute(type);
     }
 
     parseType() {
@@ -2716,8 +2715,14 @@ mlir.Parser = class {
             return { value };
         }
         // Parse an extended attribute, i.e. alias or dialect attribute.
+        // Reference: AttributeParser.cpp - parse extended attr, then optionally parse : type
         if (this.match('#')) {
-            return this.parseExtendedAttr();
+            const attr = this.parseExtendedAttr();
+            // Only parse : type suffix if type not already provided
+            if (!type && this.accept(':')) {
+                attr.type = this.parseType();
+            }
+            return attr;
         }
         const parseType = (type, defaultType) => {
             if (type) {
@@ -2869,6 +2874,8 @@ mlir.Parser = class {
         throw new mlir.Error(`Unexpected attribute token '${this._token.value}' ${this.location()}`);
     }
 
+    // Reference: DialectSymbolParser.cpp parseExtendedSymbol
+    // Parses #dialect.symbol or #alias - does NOT parse trailing : type
     parseExtendedAttr() {
         const name = this.expect('#');
         let value = name;
@@ -2879,13 +2886,7 @@ mlir.Parser = class {
             const body = this.skip('(', ')');
             value = name + body;
         }
-        // Parse an optional trailing colon type.
-        // Reference: DialectSymbolParser.cpp line 264-267
-        let type = null;
-        if (this.accept(':')) {
-            type = this.parseType();
-        }
-        return { value, type };
+        return { value };
     }
 
     parseOptionalAttribute(type) {
@@ -4485,11 +4486,11 @@ mlir.Dialect = class {
                 const attrInfo = opInfo.metadata && opInfo.metadata.attributes && opInfo.metadata.attributes.find((attr) => attr.name === refName);
                 const attrType = attrInfo ? attrInfo.type : null;
                 let attrValue = null;
+                // Pass type to suppress : type suffix parsing (it's a separate directive in assembly format)
                 if (attrType && attrType !== 'Attribute') {
                     attrValue = this._parseCustomAttributeWithFallback(parser, attrType);
                 } else {
-                    // In assembly format, don't parse : type suffix (default parseTypeSuffix = false)
-                    attrValue = parser.parseAttribute();
+                    attrValue = parser.parseAttribute(attrType || 'Attribute');
                 }
                 if (attrValue) {
                     const value = attrValue.value === undefined ? attrValue : attrValue.value;
@@ -4848,7 +4849,13 @@ mlir.Dialect = class {
                         if (parser.match('id') || parser.match('#') || parser.match('@') || parser.match('string') || parser.match('[')) {
                             const attrInfo = opInfo.metadata && opInfo.metadata.attributes && opInfo.metadata.attributes.find((attr) => attr.name === firstElem.name);
                             const attrType = attrInfo ? attrInfo.type : null;
-                            const result = this._parseCustomAttributeWithFallback(parser, attrType);
+                            // Pass type to suppress : type suffix parsing (it's a separate directive in assembly format)
+                            let result = null;
+                            if (attrType && attrType !== 'Attribute') {
+                                result = this._parseCustomAttributeWithFallback(parser, attrType);
+                            } else {
+                                result = parser.parseOptionalAttribute(attrType || 'Attribute');
+                            }
                             if (result !== null) {
                                 const value = result.value === undefined ? result : result.value;
                                 op.attributes.push({ name: firstElem.name, value });
@@ -4997,7 +5004,7 @@ mlir.Dialect = class {
                 return { value };
             }
         }
-        return parser.parseOptionalAttribute();
+        return parser.parseOptionalAttribute(type);
     }
 
     _parseOptionalAttr(parser, type) {
@@ -5968,6 +5975,7 @@ mlir.VhloDialect = class extends mlir.Dialect {
 
     constructor(operations) {
         super('vhlo', operations);
+        this.registerCustomDirective('FunctionBody', this._parseFunctionBody.bind(this));
     }
 
     parseOperation(parser, opName, op) {
@@ -5990,28 +5998,11 @@ mlir.VhloDialect = class extends mlir.Dialect {
             return true;
         }
 
-        if (opName === 'vhlo.func_v1') {
-            parser.parseOptionalVisibilityKeyword(op.attributes);
-            parser.parseSymbolName(opName, op.attributes);
-            if (parser.accept('(')) {
-                op.attributes.push({ name: 'inputs', value: parser.parseTypeList() });
-                parser.accept(')');
-            }
-            if (parser.accept('->')) {
-                const outputType = parser.parseType();
-                if (outputType) {
-                    const outputs = outputType.value === 'tuple' ? outputType.params : [outputType];
-                    op.attributes.push({ name: 'outputs', value: outputs });
-                }
-            }
-            if (parser.match('{')) {
-                parser.parseAttributeDict(op.attributes);
-            }
-            op.regions = [parser.parseRegion()];
-            return true;
-        }
+        return super.parseOperation(parser, opName, op);
+    }
 
-        return false;
+    _parseFunctionBody(parser, op) {
+        parser.parseFunctionOp(op, false);
     }
 };
 
@@ -7005,6 +6996,7 @@ mlir.HALDialect = class extends mlir.IREEDialect {
         super('hal', operations);
         this.simpleTypes = new Set(['allocator', 'buffer', 'buffer_view', 'channel', 'command_buffer', 'descriptor_set', 'descriptor_set_layout', 'device', 'event', 'executable', 'executable_layout', 'fence', 'file', 'semaphore']);
         this.registerCustomAttribute('HAL_PipelineLayoutAttr', this._parsePipelineLayoutAttr.bind(this));
+        this.registerCustomDirective('ExportConditionRegion', this._parseExportConditionRegion.bind(this));
     }
 
     parseType(parser, dialectName) {
@@ -7179,6 +7171,7 @@ mlir.HALDialect = class extends mlir.IREEDialect {
             opName !== 'hal.executable.create' &&
             opName !== 'hal.executable.export' &&
             opName !== 'hal.executable.binary' &&
+            opName !== 'hal.executable.source' &&
             opName !== 'hal.executable.condition' &&
             opName !== 'hal.executable.constant.block') {
             // Parse <%operand : type> if present
@@ -7268,7 +7261,7 @@ mlir.HALDialect = class extends mlir.IREEDialect {
             return true;
         }
         // Handle operations with visibility + symbol (similar to flow dialect)
-        if (opName === 'hal.executable' || opName === 'hal.interface' || opName === 'hal.executable.binary') {
+        if (opName === 'hal.executable' || opName === 'hal.executable.source' || opName === 'hal.interface' || opName === 'hal.executable.binary') {
             this.getOperation(opName).hasParseOperation = false; // compatibility?
             if (parser.match('id', 'private') || parser.match('id', 'public') || parser.match('id', 'nested')) {
                 parser.expect('id');
@@ -7345,6 +7338,28 @@ mlir.HALDialect = class extends mlir.IREEDialect {
                     break;
                 }
                 const paramName = parser.expect('id');
+                if (paramName === 'condition') {
+                    parser.expect('(');
+                    const regionArgs = [];
+                    while (!parser.match(')')) {
+                        const arg = parser.expect('%');
+                        let type = null;
+                        if (parser.accept(':')) {
+                            type = parser.parseType();
+                        }
+                        regionArgs.push({ value: arg, type });
+                        if (!parser.accept(',')) {
+                            break;
+                        }
+                    }
+                    parser.expect(')');
+                    parser.expect('->');
+                    parser.parseType();
+                    const conditionRegion = { arguments: regionArgs };
+                    parser.parseRegion(conditionRegion);
+                    op.regions.push(conditionRegion);
+                    continue;
+                }
                 if (parser.accept('(')) {
                     let parenDepth = 1;
                     let paramValue = '';
@@ -7411,6 +7426,33 @@ mlir.HALDialect = class extends mlir.IREEDialect {
         }
         return parser.parseOptionalAttribute();
     }
+
+    _parseExportConditionRegion(parser, op, args) {
+        parser.expect('(');
+        const regionArgs = [];
+        while (!parser.match(')')) {
+            const arg = parser.expect('%');
+            let type = null;
+            if (parser.accept(':')) {
+                type = parser.parseType();
+            }
+            regionArgs.push({ value: arg, type });
+            if (!parser.accept(',')) {
+                break;
+            }
+        }
+        parser.expect(')');
+        parser.expect('->');
+        parser.parseType();
+        const region = { arguments: regionArgs };
+        parser.parseRegion(region);
+        if (args && args.length >= 1) {
+            const regionName = args[0].replace('$', '');
+            if (regionName === 'condition' && op.regions) {
+                op.regions.push(region);
+            }
+        }
+    }
 };
 
 mlir.UtilDialect = class extends mlir.IREEDialect {
@@ -7554,11 +7596,45 @@ mlir.UtilDialect = class extends mlir.IREEDialect {
         const argResult = parser.parseFunctionArgumentList(false);
         const resultTypes = [];
         const resultAttrs = [];
+        const tiedOperandIndices = [];
+        // Reference: UtilOps.cpp parseTiedFunctionResultList
+        // Parse result list which may contain:
+        // - Regular type: tensor<...>
+        // - Tied reference: %arg1 (inherits type from argument)
+        // - Tied with type override: %arg2 as tensor<...>
+        const parseTiedResultOrType = () => {
+            // Check if this is a tied operand reference (%argN)
+            if (parser.match('%')) {
+                const tiedRef = parser.expect('%');
+                // Find the argument index
+                let tiedIndex = -1;
+                for (let i = 0; i < argResult.arguments.length; i++) {
+                    if (argResult.arguments[i].value === tiedRef) {
+                        tiedIndex = i;
+                        break;
+                    }
+                }
+                tiedOperandIndices.push(tiedIndex);
+                // Check for 'as type' override
+                if (parser.accept('id', 'as')) {
+                    return parser.parseType();
+                }
+                // Use the argument's type
+                if (tiedIndex >= 0 && argResult.arguments[tiedIndex].type) {
+                    return argResult.arguments[tiedIndex].type;
+                }
+                // Fallback: return a placeholder type
+                return new mlir.Type('!util.unknown');
+            }
+            // Regular type
+            tiedOperandIndices.push(-1);
+            return parser.parseType();
+        };
         if (parser.accept('->')) {
             if (parser.accept('(')) {
                 if (!parser.match(')')) {
                     do {
-                        resultTypes.push(parser.parseType());
+                        resultTypes.push(parseTiedResultOrType());
                         if (parser.match('{')) {
                             const attrList = [];
                             parser.parseAttributeDict(attrList);
@@ -7571,7 +7647,7 @@ mlir.UtilDialect = class extends mlir.IREEDialect {
                 parser.expect(')');
             } else {
                 do {
-                    resultTypes.push(parser.parseType());
+                    resultTypes.push(parseTiedResultOrType());
                     resultAttrs.push(null);
                 } while (parser.accept(','));
             }
@@ -7579,6 +7655,9 @@ mlir.UtilDialect = class extends mlir.IREEDialect {
         const argTypes = argResult.arguments.filter((a) => a.value !== '...').map((a) => a.type);
         const type = { inputs: argTypes, results: resultTypes };
         op.attributes.push({ name: 'function_type', value: type });
+        if (tiedOperandIndices.some((i) => i >= 0)) {
+            op.attributes.push({ name: 'tied_operands', value: tiedOperandIndices });
+        }
         if (resultAttrs.some((a) => a !== null)) {
             op.attributes.push({ name: 'res_attrs', value: resultAttrs });
         }
@@ -7750,6 +7829,8 @@ mlir.FlowDialect = class extends mlir.IREEDialect {
             parser.parseRegion(region);
             op.regions.push(region);
         }
+        // Parse optional count region
+        this._parseDispatchWorkgroupsCountRegion(parser, op);
         return true;
     }
 
@@ -8511,14 +8592,24 @@ mlir.StreamDialect = class extends mlir.IREEDialect {
             return new mlir.Type(type);
         }
         // Handle test.fence type (Stream_TestFence in StreamTypes.td)
+        // Reference: mnemonic = "test.fence" means the type name after stream. is "test.fence"
         if (typeName === 'test') {
-            parser.expect('.');
-            const subtype = parser.parseKeyword();
-            if (subtype === 'fence') {
-                return new mlir.Type(`!${dialectName}.test.fence`);
+            if (parser.accept('.')) {
+                const subtype = parser.parseKeyword();
+                if (subtype === 'fence') {
+                    return new mlir.Type(`!${dialectName}.test.fence`);
+                }
+                // Handle unknown test.X subtypes generically
+                return new mlir.Type(`!${dialectName}.test.${subtype}`);
             }
+            // Just "test" without subtype - return as is
+            return new mlir.Type(type);
         }
-        return null;
+        // Fallback for unknown stream types - parse generically like base Dialect
+        if (parser.match('<')) {
+            type += parser.skip('<', '>');
+        }
+        return new mlir.Type(type);
     }
 
     parseOperation(parser, opName, op) {
@@ -9490,6 +9581,10 @@ mlir.TosaDialect = class extends mlir.Dialect {
             }
             return new mlir.Type(type);
         }
+        // Reference: TosaTypesBase.td - mxint8 is a simple type without parameters
+        if (typeName === 'mxint8') {
+            return new mlir.Type(`!${dialectName}.mxint8`);
+        }
         return null;
     }
 
@@ -9844,6 +9939,55 @@ mlir.SPIRVDialect = class extends mlir.Dialect {
             }
             return true;
         }
+        if (opName === 'spirv.SpecConstantComposite' || opName === 'spv.SpecConstantComposite') {
+            const symName = parser.expect('@');
+            op.attributes.push({ name: 'sym_name', value: symName });
+            parser.expect('(');
+            const constituents = [];
+            while (!parser.match(')')) {
+                if (parser.match('@')) {
+                    constituents.push(parser.expect('@'));
+                }
+                if (!parser.accept(',')) {
+                    break;
+                }
+            }
+            parser.expect(')');
+            op.attributes.push({ name: 'constituents', value: constituents });
+            if (parser.accept(':')) {
+                const type = parser.parseType();
+                op.attributes.push({ name: 'type', value: type.toString() });
+            }
+            return true;
+        }
+        if (opName.endsWith('.SpecConstantCompositeReplicate')) {
+            const symName = parser.expect('@');
+            op.attributes.push({ name: 'sym_name', value: symName });
+            parser.expect('(');
+            if (parser.match('@')) {
+                const constituent = parser.expect('@');
+                op.attributes.push({ name: 'constituent', value: constituent });
+            }
+            parser.expect(')');
+            if (parser.accept(':')) {
+                const type = parser.parseType();
+                op.attributes.push({ name: 'type', value: type.toString() });
+            }
+            return true;
+        }
+        if (opName === 'spirv.SpecConstantOperation' || opName === 'spv.SpecConstantOperation') {
+            parser.expect('id', 'wraps');
+            const wrappedOp = parser.parseGenericOperation();
+            if (wrappedOp) {
+                const region = { blocks: [{ operations: [wrappedOp] }] };
+                op.regions.push(region);
+                if (wrappedOp.results && wrappedOp.results.length > 0) {
+                    op.results.push({ type: wrappedOp.results[0].type });
+                }
+            }
+            parser.parseOptionalAttrDict(op.attributes);
+            return true;
+        }
         if (opName === 'spirv.Constant' || opName === 'spv.Constant') {
             const value = parser.parseAttribute();
             if (parser.accept(':')) {
@@ -10617,6 +10761,20 @@ mlir.EmitCDialect = class extends mlir.Dialect {
             }
             return true;
         }
+        if (opName === 'emitc.if') {
+            const cond = parser.expect('%');
+            op.operands.push({ value: cond });
+            const thenRegion = {};
+            parser.parseRegion(thenRegion);
+            op.regions.push(thenRegion);
+            if (parser.accept('id', 'else')) {
+                const elseRegion = {};
+                parser.parseRegion(elseRegion);
+                op.regions.push(elseRegion);
+            }
+            parser.parseOptionalAttrDict(op.attributes);
+            return true;
+        }
         return super.parseOperation(parser, opName, op);
     }
 
@@ -10685,27 +10843,36 @@ mlir.AsyncDialect = class extends mlir.Dialect {
 
     parseType(parser, dialectName) {
         const typeName = parser.parseKeyword();
-        if (typeName) {
-            let type = `!${dialectName}.${typeName}`;
-            if (typeName === 'coro' && parser.match('.')) {
-                parser.expect('.');
-                const subType = parser.expect('id');
-                type += `.${subType}`;
-                return new mlir.Type(type);
-            }
-            const simpleTypes = ['token', 'group'];
-            if (simpleTypes.includes(typeName)) {
-                return new mlir.Type(type);
-            }
-            if (typeName === 'value') {
-                if (parser.match('<')) {
-                    const content = parser.skip('<', '>');
-                    type += content;
+        if (!typeName) {
+            return null;
+        }
+        let type = `!${dialectName}.${typeName}`;
+        // Handle coro.* types (coro.id, coro.handle, coro.state)
+        if (typeName === 'coro') {
+            if (parser.accept('.')) {
+                const subType = parser.parseKeyword();
+                if (subType) {
+                    type += `.${subType}`;
                 }
-                return new mlir.Type(type);
             }
+            return new mlir.Type(type);
         }
-        return null;
+        const simpleTypes = ['token', 'group'];
+        if (simpleTypes.includes(typeName)) {
+            return new mlir.Type(type);
+        }
+        if (typeName === 'value') {
+            if (parser.match('<')) {
+                const content = parser.skip('<', '>');
+                type += content;
+            }
+            return new mlir.Type(type);
+        }
+        // Fallback for unknown async types
+        if (parser.match('<')) {
+            type += parser.skip('<', '>');
+        }
+        return new mlir.Type(type);
     }
 
     parseOperation(parser, opName, op) {
@@ -11548,14 +11715,13 @@ mlir.SparseTensorDialect = class extends mlir.Dialect {
     }
 
     _parseLevelRange(parser, op, args) {
-        const start = parser.parseInteger();
-        parser.expect('id', 'to');
-        const end = parser.parseInteger();
+        const loLvl = parser.parseInteger();
+        const hiLvl = parser.accept('id', 'to') ? parser.parseInteger() : loLvl + 1;
         if (args && args.length >= 2) {
             const startAttr = args[0].replace('$', '');
             const endAttr = args[1].replace('$', '');
-            op.attributes.push({ name: startAttr, value: start });
-            op.attributes.push({ name: endAttr, value: end });
+            op.attributes.push({ name: startAttr, value: loLvl });
+            op.attributes.push({ name: endAttr, value: hiLvl });
         }
     }
 
@@ -13353,7 +13519,7 @@ mlir.LLVMDialect = class extends mlir.Dialect {
         parser.parseOptionalAttrDictWithKeyword(op.attributes);
         if (parser.match('{')) {
             const region = {};
-            parser.parseRegion(region);
+            parser.parseRegion(region, argResult.arguments);
             op.regions.push(region);
         }
         return true;
@@ -13916,7 +14082,7 @@ mlir.TFRTDialect = class extends mlir.Dialect {
             return null;
         }
         let type = `!${dialectName}.${typeName}`;
-        const simpleTypes = ['chain', 'string', 'dist_context'];
+        const simpleTypes = ['chain', 'string', 'dist_context', 'device', 'tensor_type'];
         if (simpleTypes.includes(typeName)) {
             return new mlir.Type(type);
         }
@@ -13927,7 +14093,11 @@ mlir.TFRTDialect = class extends mlir.Dialect {
             }
             return new mlir.Type(type);
         }
-        return null;
+        // Fallback for unknown tfrt types
+        if (parser.match('<')) {
+            type += parser.skip('<', '>');
+        }
+        return new mlir.Type(type);
     }
 
     parseOperation(parser, opName, op) {
@@ -14870,8 +15040,9 @@ mlir.TFTypeDialect = class extends mlir.Dialect {
         if (!typeName) {
             return null;
         }
-        const type = `!${dialectName}.${typeName}`;
-        if (typeName === 'resource' || typeName === 'variant') {
+        let type = `!${dialectName}.${typeName}`;
+        // Handle parametrized types like resource<>, variant<>, resource_handle<>
+        if (typeName === 'resource' || typeName === 'variant' || typeName === 'resource_handle') {
             if (parser.accept('<')) {
                 const subtypes = [];
                 while (!parser.match('>')) {
@@ -14879,20 +15050,38 @@ mlir.TFTypeDialect = class extends mlir.Dialect {
                     parser.accept(',');
                 }
                 parser.expect('>');
-                return `${type}<${subtypes.join(', ')}>`;
+                return new mlir.Type(`${type}<${subtypes.join(', ')}>`);
             }
             return new mlir.Type(type);
         }
         if (this.simpleTypes.has(typeName)) {
             return new mlir.Type(type);
         }
-        return null;
+        // Fallback for unknown tf_type types
+        if (parser.match('<')) {
+            type += parser.skip('<', '>');
+        }
+        return new mlir.Type(type);
     }
 };
 
 mlir.CheckDialect = class extends mlir.Dialect {
     constructor(operations) {
         super('check', operations);
+        // Workaround: Handle conflicting dialects from stablehlo and iree
+        for (const [name] of this._operations.entries()) {
+            this._operations.set(name.replace(/<(stablehlo|iree)>\./, ''), { metadata: {} });
+        }
+    }
+
+    parseOperation(parser, opName, op) {
+        // Workaround: Handle conflicting dialects from stablehlo and iree
+        let dialect = 'stablehlo';
+        if (parser.match('(') || parser.match('<')) {
+            dialect = 'iree';
+        }
+        opName = opName.replace('check.', `check.<${dialect}>.`);
+        return super.parseOperation(parser, opName, op);
     }
 };
 
@@ -14920,100 +15109,6 @@ mlir.TransformDialect = class extends mlir.Dialect {
             }
             return true;
         }
-        // C++-only operation: transform.iree.register_match_callbacks
-        // Used in IREE tests for registering pattern matching callbacks
-        if (opName === 'transform.iree.register_match_callbacks') {
-            // No operands or attributes
-            return true;
-        }
-        // C++-only operation: transform.iree.match_callback failures(propagate) "name"(%op) : (types) -> (types)
-        if (opName === 'transform.iree.match_callback') {
-            // Parse optional failures(propagate)
-            if (parser.accept('id', 'failures')) {
-                parser.expect('(');
-                parser.expect('id'); // propagate, suppress, etc.
-                parser.expect(')');
-            }
-            // Parse callback name
-            if (parser.match('string')) {
-                const callbackName = parser.expect('string');
-                op.attributes.push({ name: 'callback_name', value: callbackName });
-            }
-            // Parse operands
-            if (parser.accept('(')) {
-                while (!parser.match(')')) {
-                    if (parser.match('%')) {
-                        op.operands.push({ value: parser.expect('%') });
-                    }
-                    if (!parser.accept(',')) {
-                        break;
-                    }
-                }
-                parser.expect(')');
-            }
-            // Parse : (input_types) -> (result_types)
-            if (parser.accept(':')) {
-                parser.expect('(');
-                while (!parser.match(')')) {
-                    parser.parseType();
-                    if (!parser.accept(',')) {
-                        break;
-                    }
-                }
-                parser.expect(')');
-                if (parser.accept('->')) {
-                    parser.expect('(');
-                    const resultTypes = [];
-                    while (!parser.match(')')) {
-                        resultTypes.push(parser.parseType());
-                        if (!parser.accept(',')) {
-                            break;
-                        }
-                    }
-                    parser.expect(')');
-                    for (const type of resultTypes) {
-                        op.results.push({ type });
-                    }
-                }
-            }
-            return true;
-        }
-        // C++-only operation: transform.iree.take_first %op1, %op2 : (types) -> (types)
-        if (opName === 'transform.iree.take_first') {
-            // Parse operands
-            while (parser.match('%')) {
-                op.operands.push({ value: parser.expect('%') });
-                if (!parser.accept(',')) {
-                    break;
-                }
-            }
-            // Parse : (input_types) -> (result_types)
-            if (parser.accept(':')) {
-                parser.expect('(');
-                while (!parser.match(')')) {
-                    parser.parseType();
-                    if (!parser.accept(',')) {
-                        break;
-                    }
-                }
-                parser.expect(')');
-                if (parser.accept('->')) {
-                    parser.expect('(');
-                    const resultTypes = [];
-                    while (!parser.match(')')) {
-                        resultTypes.push(parser.parseType());
-                        if (!parser.accept(',')) {
-                            break;
-                        }
-                    }
-                    parser.expect(')');
-                    for (const type of resultTypes) {
-                        op.results.push({ type });
-                    }
-                }
-            }
-            return true;
-        }
         return super.parseOperation(parser, opName, op);
     }
 

+ 1 - 1
tools/mlir

@@ -15,7 +15,7 @@ entries=(
     "https://github.com/tensorflow/tensorflow.git|master|${src_dir}/tensorflow|tensorflow/compiler/mlir"
     "https://github.com/tensorflow/runtime.git|master|${src_dir}/runtime|"
     "https://github.com/NVIDIA/TensorRT-Incubator.git|main|${src_dir}/TensorRT-Incubator|mlir-tensorrt"
-    "https://github.com/iree-org/iree.git|main|${src_dir}/iree|compiler"
+    "https://github.com/iree-org/iree.git|main|${src_dir}/iree|"
     "https://github.com/monellz/FlashTensor.git|main|${src_dir}/FlashTensor|"
     "https://github.com/plaidml/plaidml.git|plaidml-v1|${src_dir}/plaidml|pmlc"
     "https://github.com/sophgo/tpu-mlir.git|master|${src_dir}/tpu-mlir|include"

+ 17 - 1
tools/mlir-script.js

@@ -73,6 +73,7 @@ const schema = async () => {
         path.join(source, 'iree', 'compiler', 'src'),
         path.join(source, 'iree', 'compiler', 'src', 'iree', 'compiler', 'Codegen', 'Dialect', 'PCF', 'IR'),
         path.join(source, 'iree', 'compiler', 'src', 'iree', 'compiler', 'Modules', 'IO', 'Parameters', 'IR'),
+        path.join(source, 'iree', 'llvm-external-projects', 'iree-dialects', 'include'),
         path.join(source, 'FlashTensor', 'include'),
         path.join(source, 'tpu-mlir', 'include'),
         path.join(source, 'tensorflow'),
@@ -263,6 +264,8 @@ const schema = async () => {
         'iree/compiler/Dialect/VM/IR/VMOps.td',
         'iree/compiler/Dialect/VMVX/IR/VMVXOps.td',
         'iree/compiler/Dialect/Encoding/IR/EncodingOps.td',
+        'iree/compiler/src/iree/compiler/Modules/Check/IR/CheckOps.td',
+        'iree-dialects/Dialect/LinalgTransform/StructuredTransformOpsExt.td',
         'asuka/Dialect/Asuka/IR/AsukaOps.td',
         'tpu_mlir/Dialect/Top/IR/TopOps.td',
         'tpu_mlir/Dialect/Tpu/IR/TpuOps.td',
@@ -311,13 +314,21 @@ const schema = async () => {
     await parser.parse(dialects, paths);
     for (const def of parser.defs) {
         const op = new Operator(def);
-        const operationName = op.getOperationName();
+        let operationName = op.getOperationName();
         if (!operationName) {
             continue;
         }
         if (operationName.endsWith('.') || operationName.includes('..') || operationName.includes('#')) {
             throw new Error(`Invalid operation name '${operationName}'.`);
         }
+        // Workaround: Handle conflicting dialects from stablehlo and iree
+        if (operationName.startsWith('check.')) {
+            if (def.location.file.includes('stablehlo')) {
+                operationName = operationName.replace(/^check./, 'check.<stablehlo>.');
+            } else if (def.location.file.includes('iree')) {
+                operationName = operationName.replace(/^check./, 'check.<iree>.');
+            }
+        }
         const operation = {
             name: operationName
         };
@@ -625,6 +636,11 @@ const test = async (pattern) => {
         'third_party/source/mlir/stablehlo/stablehlo/tests/ops_stablehlo.mlir',
         'third_party/source/mlir/stablehlo/stablehlo/tests/print_types_invalid.mlir',
         'third_party/source/mlir/stablehlo/stablehlo/tests/vhlo/invalid_vhlo_future.mlir',
+        'third_party/source/mlir/tensorflow/tensorflow/compiler/mlir/tensorflow/tests/tf_executor_ops_invalid.mlir',
+        'third_party/source/mlir/llvm-project/mlir/test/Dialect/Quant/parse-uniform-invalid.mlir',
+        'third_party/source/mlir/mlir-dace/design/mlir/map.mlir',
+        'third_party/source/mlir/mlir-dace/design/mlir/simple_sdfg.mlir',
+        'third_party/source/mlir/mlir-dace/design/mlir/symbol.mlir',
     ]);
     return new Promise((resolve, reject) => {
         const cmd = 'node';