[Mlir-commits] [mlir] [mlir][func] Fix crash when a C interface wrapper cannot be built (PR #216489)

Hamza Qureshi llvmlistbot at llvm.org
Sat Aug 15 06:24:25 PDT 2026


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

Fixes #216268

`mlir-opt --gpu-to-llvm` crashes on this input:

```mlir
func.func @m0(%arg0: !async.token) attributes {llvm.emit_c_interface} {
  return
}
```

`llvm.emit_c_interface` asks for an extra wrapper function so the function can be called from C. The wrapper signature is built by `convertFunctionTypeCWrapper`, which requires every argument type to be LLVM compatible and returns nothing otherwise. That is stricter than the signature conversion, so it can fail for a function whose signature converted fine.

Nothing checked for that. The empty result was passed to `LLVMFuncOp::create`, which built a `TypeAttr` from a null type and crashed.

This checks the wrapper signature next to the existing varargs check, before any IR is created, so the pattern fails cleanly and the function is left unconverted.

Note the wrapper path for external functions only had an assert here, with a comment saying the conversion could not fail because it would have failed earlier. That assumption does not hold: the signature conversion and the C wrapper conversion do not accept the same types.

Testing: added `mlir/test/Conversion/GPUCommon/emit-c-interface-invalid.mlir` covering a function with a body, an external function, and a function that must still get its wrapper. On an assertions build the reproducer segfaults before the change and exits cleanly after, the new test fails without the change and passes with it, and `mlir/test/Conversion` stays at 413/413.

>From 16035f1f0efae0f6c3a1feca10b03bccbced448f Mon Sep 17 00:00:00 2001
From: hamzaqureshi5 <hamza7771.861 at gmail.com>
Date: Sat, 15 Aug 2026 18:09:32 +0500
Subject: [PATCH] [mlir][func] Fix crash when a C interface wrapper cannot be
 built

Functions marked with llvm.emit_c_interface get a wrapper function whose
signature is built by convertFunctionTypeCWrapper. That conversion requires
every argument type to be LLVM compatible, which is stricter than the
signature conversion, so it can fail for a function whose signature converted
fine. Its null result then reached LLVMFuncOp::create, which crashed building
a TypeAttr from it.

Check the wrapper signature next to the existing varargs check, before any IR
is created, so the pattern fails without leaving the function half converted.

Fixes #216268
---
 mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp |  9 +++++
 .../GPUCommon/emit-c-interface-invalid.mlir   | 34 +++++++++++++++++++
 2 files changed, 43 insertions(+)
 create mode 100644 mlir/test/Conversion/GPUCommon/emit-c-interface-invalid.mlir

diff --git a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
index 2686158f67e76..1b6bbf5fe910c 100644
--- a/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
+++ b/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp
@@ -472,6 +472,15 @@ FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
     return funcOp.emitError("C interface for variadic functions is not "
                             "supported yet.");
 
+  // The C wrapper signature requires all argument types to be LLVM compatible,
+  // which is stricter than the signature conversion above. Check it here, so
+  // that the pattern fails before any IR is created rather than while emitting
+  // the wrapper.
+  if (!useBarePtrCallConv && emitCWrapper &&
+      !converter.convertFunctionTypeCWrapper(funcTy).first)
+    return rewriter.notifyMatchFailure(
+        funcOp, "failed to convert C interface wrapper function type");
+
   // Lower function attrs
   FailureOr<LoweredLLVMFuncAttrs> loweredAttrs =
       lowerDiscardableAttrsForLLVMFunc(funcOp, *llvmType);
diff --git a/mlir/test/Conversion/GPUCommon/emit-c-interface-invalid.mlir b/mlir/test/Conversion/GPUCommon/emit-c-interface-invalid.mlir
new file mode 100644
index 0000000000000..3e77541e1ef5f
--- /dev/null
+++ b/mlir/test/Conversion/GPUCommon/emit-c-interface-invalid.mlir
@@ -0,0 +1,34 @@
+// RUN: mlir-opt %s --gpu-to-llvm -split-input-file | FileCheck %s
+
+// The C interface wrapper signature requires every argument type to be LLVM
+// compatible, which is stricter than the plain signature conversion: this
+// pass converts the function signature but cannot build a wrapper for it.
+// The function must be left unconverted instead of creating a wrapper with a
+// null function type.
+
+// CHECK-LABEL: func.func @ciface_unsupported_arg
+// CHECK-NOT:     _mlir_ciface_
+func.func @ciface_unsupported_arg(%arg0: !async.token)
+    attributes {llvm.emit_c_interface} {
+  return
+}
+
+// -----
+
+// Same for an external function, which takes the other wrapper code path.
+
+// CHECK-LABEL: func.func private @ciface_unsupported_arg_decl
+// CHECK-NOT:     _mlir_ciface_
+func.func private @ciface_unsupported_arg_decl(%arg0: !async.token)
+    attributes {llvm.emit_c_interface}
+
+// -----
+
+// A function whose arguments do convert must still get its wrapper.
+
+// CHECK-LABEL: llvm.func @ciface_supported_arg
+// CHECK:       llvm.func @_mlir_ciface_ciface_supported_arg
+func.func @ciface_supported_arg(%arg0: memref<4xf32>)
+    attributes {llvm.emit_c_interface} {
+  return
+}



More information about the Mlir-commits mailing list