[Mlir-commits] [mlir] [mlir][async] Fix crash when lowering async values of non-LLVM types (PR #216487)

Hamza Qureshi llvmlistbot at llvm.org
Sat Aug 15 05:54:13 PDT 2026


https://github.com/hamzaqureshi5 created https://github.com/llvm/llvm-project/pull/216487

Fixes #216213

## The problem

`mlir-opt -convert-async-to-llvm` segfaults on valid input:

```mlir
func.func @test() -> !async.value<tensor<4xf32>> {
  %0 = async.runtime.create : !async.value<tensor<4xf32>>
  return %0 : !async.value<tensor<4xf32>>
}
```

To create an async value the lowering needs to know how much storage the payload takes, which it computes by emitting a GEP over the converted payload type:

```cpp
auto storedType = converter->convertType(valueType.getValueType());
...
LLVM::GEPOp::create(rewriter, loc, storagePtrType, storedType, ...);
```

`tensor<4xf32>` has no LLVM counterpart, so `convertType` returns a null `Type`. That null then reaches `TypeAttr::get`, which calls `Type::getContext()` on it, and the pass crashes.

## The fix

Check the converted type and bail out if the payload cannot be represented:

```cpp
Type storedType = converter->convertType(value.getValueType());
if (!storedType)
  return rewriter.notifyMatchFailure(
      op, "failed to convert async value type to LLVM type");
```

The `RuntimeStoreOp` and `RuntimeLoadOp` patterns in this same file already perform this check; `RuntimeCreateOpLowering` was missing it.

With the fix, the op is left unconverted and the conversion framework reports it normally:

```
error: failed to legalize operation 'async.runtime.create' that was explicitly marked illegal
```

## Testing

Added `mlir/test/Conversion/AsyncToLLVM/convert-runtime-to-llvm-invalid.mlir` covering two non-convertible payload types.

Verified with an assertions-enabled build:
- the reproducer segfaults before the fix and reports a clean error after
- the new test fails without the fix and passes with it
- async values with convertible payloads (`!async.value<f32>`) still lower to `mlirAsyncRuntimeCreateValue`
- `mlir/test/Conversion/AsyncToLLVM` and `mlir/test/Dialect/Async` are 18/18, no regressions

>From 78760cedf236604ffc8748613a5462acf079c24b Mon Sep 17 00:00:00 2001
From: hamzaqureshi5 <hamza7771.861 at gmail.com>
Date: Sat, 15 Aug 2026 17:52:19 +0500
Subject: [PATCH] [mlir][async] Fix crash when lowering async values of
 non-LLVM types

RuntimeCreateOpLowering computes the storage requirement of an async value by
emitting a GEP over the converted payload type. The result of the type
conversion was used without checking it, so a payload type with no LLVM
counterpart produced a null Type that reached TypeAttr::get, and the pass
crashed instead of reporting a failure.

Bail out when the payload type cannot be converted, matching the checks the
RuntimeStoreOp and RuntimeLoadOp patterns in this file already perform.

Fixes #216213
---
 .../Conversion/AsyncToLLVM/AsyncToLLVM.cpp    | 12 +++++++++---
 .../convert-runtime-to-llvm-invalid.mlir      | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+), 3 deletions(-)
 create mode 100644 mlir/test/Conversion/AsyncToLLVM/convert-runtime-to-llvm-invalid.mlir

diff --git a/mlir/lib/Conversion/AsyncToLLVM/AsyncToLLVM.cpp b/mlir/lib/Conversion/AsyncToLLVM/AsyncToLLVM.cpp
index 46e53e71d35f5..a143876c1f357 100644
--- a/mlir/lib/Conversion/AsyncToLLVM/AsyncToLLVM.cpp
+++ b/mlir/lib/Conversion/AsyncToLLVM/AsyncToLLVM.cpp
@@ -592,12 +592,18 @@ class RuntimeCreateOpLowering : public ConvertOpToLLVMPattern<RuntimeCreateOp> {
 
     // To create a value we need to compute the storage requirement.
     if (auto value = dyn_cast<ValueType>(resultType)) {
+      // Computing the storage requirement emits a GEP over the stored type, so
+      // the payload type must have an LLVM counterpart.
+      Type storedType = converter->convertType(value.getValueType());
+      if (!storedType)
+        return rewriter.notifyMatchFailure(
+            op, "failed to convert async value type to LLVM type");
+
       // Returns the size requirements for the async value storage.
-      auto sizeOf = [&](ValueType valueType) -> Value {
+      auto sizeOf = [&]() -> Value {
         auto loc = op->getLoc();
         auto i64 = rewriter.getI64Type();
 
-        auto storedType = converter->convertType(valueType.getValueType());
         auto storagePtrType =
             AsyncAPI::opaquePointerType(rewriter.getContext());
 
@@ -611,7 +617,7 @@ class RuntimeCreateOpLowering : public ConvertOpToLLVMPattern<RuntimeCreateOp> {
       };
 
       rewriter.replaceOpWithNewOp<func::CallOp>(op, kCreateValue, resultType,
-                                                sizeOf(value));
+                                                sizeOf());
 
       return success();
     }
diff --git a/mlir/test/Conversion/AsyncToLLVM/convert-runtime-to-llvm-invalid.mlir b/mlir/test/Conversion/AsyncToLLVM/convert-runtime-to-llvm-invalid.mlir
new file mode 100644
index 0000000000000..f04ec00268831
--- /dev/null
+++ b/mlir/test/Conversion/AsyncToLLVM/convert-runtime-to-llvm-invalid.mlir
@@ -0,0 +1,19 @@
+// RUN: mlir-opt %s -convert-async-to-llvm -split-input-file -verify-diagnostics
+
+// Computing the storage size of an async value emits a GEP over the converted
+// payload type. Payload types without an LLVM counterpart must be rejected
+// instead of building the GEP with a null type.
+
+func.func @create_value_unsupported_payload() {
+  // expected-error @below {{failed to legalize operation 'async.runtime.create'}}
+  %0 = async.runtime.create : !async.value<tensor<4xf32>>
+  return
+}
+
+// -----
+
+func.func @create_value_unsupported_payload_index() {
+  // expected-error @below {{failed to legalize operation 'async.runtime.create'}}
+  %0 = async.runtime.create : !async.value<tensor<?xindex>>
+  return
+}



More information about the Mlir-commits mailing list