[Mlir-commits] [mlir] 0a9cfa0 - [mlir][llvm] Add operations for coroutine intrinsics (#214099)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Aug 6 09:14:16 PDT 2026


Author: Andy Kaylor
Date: 2026-08-06T09:14:11-07:00
New Revision: 0a9cfa09242aa1cc162e55ddf1bbcc50664d763d

URL: https://github.com/llvm/llvm-project/commit/0a9cfa09242aa1cc162e55ddf1bbcc50664d763d
DIFF: https://github.com/llvm/llvm-project/commit/0a9cfa09242aa1cc162e55ddf1bbcc50664d763d.diff

LOG: [mlir][llvm] Add operations for coroutine intrinsics (#214099)

This adds operations to the LLVM dialect to represent coroutine
intrinsics that were previously missing from the dialect. A few
coroutine operations were already in place. This change adds operations
for the remaining intrinsics that cab be generated by Clang. There are
some additional coroutine intrinsics defined in LLVM IR that aren't
covered, but I'm omitting those until they are needed.

We are in the process of implementing coroutine support in CIR, and
these are the operations we'll need for lowering to LLVM.

Assisted-by: Cursor / Grok 4.5

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
    mlir/test/Target/LLVMIR/Import/intrinsic.ll
    mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index 464779e006d7f..cbd513e1a4021 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -721,6 +721,12 @@ def LLVM_CoroBeginOp
   let assemblyFormat = "$token `,` $mem attr-dict `:` functional-type(operands, results)";
 }
 
+def LLVM_CoroAllocOp
+    : LLVM_IntrOp<"coro.alloc", [], [], [], 1> {
+  let arguments = (ins Token:$id);
+  let assemblyFormat = "$id attr-dict `:` functional-type(operands, results)";
+}
+
 def LLVM_CoroSizeOp : LLVM_IntrOp<"coro.size", [0], [], [], 1> {
   let assemblyFormat = "attr-dict `:` type($res)";
 }
@@ -770,6 +776,70 @@ def LLVM_CoroPromiseOp : LLVM_IntrOp<"coro.promise", [], [], [], 1> {
   let assemblyFormat = "$handle `,` $align `,` $from attr-dict `:` functional-type(operands, results)";
 }
 
+def LLVM_CoroFrameOp : LLVM_IntrOp<"coro.frame", [], [], [], 1> {
+  let results = (outs LLVM_AnyPointer:$res);
+  let assemblyFormat = "attr-dict `:` type($res)";
+}
+
+def LLVM_CoroNoopOp : LLVM_IntrOp<"coro.noop", [], [], [], 1> {
+  let results = (outs LLVM_AnyPointer:$res);
+  let assemblyFormat = "attr-dict `:` type($res)";
+}
+
+def LLVM_CoroDestroyOp : LLVM_IntrOp<"coro.destroy", [], [], [], 0> {
+  let arguments = (ins LLVM_AnyPointer:$handle);
+  let assemblyFormat = "$handle attr-dict `:` qualified(type($handle))";
+}
+
+def LLVM_CoroDoneOp : LLVM_IntrOp<"coro.done", [], [], [], 1> {
+  let arguments = (ins LLVM_AnyPointer:$handle);
+  let assemblyFormat = "$handle attr-dict `:` functional-type(operands, results)";
+}
+
+def LLVM_CoroIsInRampOp : LLVM_IntrOp<"coro.is_in_ramp", [], [], [], 1> {
+  let assemblyFormat = "attr-dict `:` type($res)";
+}
+
+def LLVM_CoroDeadOp : LLVM_IntrOp<"coro.dead", [], [], [], 0> {
+  let arguments = (ins LLVM_AnyPointer:$handle);
+  let assemblyFormat = "$handle attr-dict `:` qualified(type($handle))";
+}
+
+def LLVM_CoroAwaitSuspendVoidOp
+    : LLVM_IntrOp<"coro.await.suspend.void", [], [], [], 0> {
+  let arguments = (ins LLVM_AnyPointer:$awaiter,
+                       LLVM_AnyPointer:$frame,
+                       LLVM_AnyPointer:$wrapper);
+  let assemblyFormat = [{
+    $awaiter `,` $frame `,` $wrapper attr-dict `:`
+      qualified(type($awaiter)) `,` qualified(type($frame)) `,`
+      qualified(type($wrapper))
+  }];
+}
+
+def LLVM_CoroAwaitSuspendBoolOp
+    : LLVM_IntrOp<"coro.await.suspend.bool", [], [], [], 1> {
+  let arguments = (ins LLVM_AnyPointer:$awaiter,
+                       LLVM_AnyPointer:$frame,
+                       LLVM_AnyPointer:$wrapper);
+  let assemblyFormat = [{
+    $awaiter `,` $frame `,` $wrapper attr-dict `:`
+      functional-type(operands, results)
+  }];
+}
+
+def LLVM_CoroAwaitSuspendHandleOp
+    : LLVM_IntrOp<"coro.await.suspend.handle", [], [], [], 0> {
+  let arguments = (ins LLVM_AnyPointer:$awaiter,
+                       LLVM_AnyPointer:$frame,
+                       LLVM_AnyPointer:$wrapper);
+  let assemblyFormat = [{
+    $awaiter `,` $frame `,` $wrapper attr-dict `:`
+      qualified(type($awaiter)) `,` qualified(type($frame)) `,`
+      qualified(type($wrapper))
+  }];
+}
+
 //
 // Debug function intrinsics.
 //

diff  --git a/mlir/test/Target/LLVMIR/Import/intrinsic.ll b/mlir/test/Target/LLVMIR/Import/intrinsic.ll
index d50a5e39bbe3a..c47d1cca187b7 100644
--- a/mlir/test/Target/LLVMIR/Import/intrinsic.ll
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic.ll
@@ -818,6 +818,15 @@ define void @coro_begin(ptr %0) {
   ret void
 }
 
+; CHECK-LABEL:  llvm.func @coro_alloc
+define void @coro_alloc() {
+  ; CHECK: %[[CORO_ID:.*]] = llvm.intr.coro.id %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}} : (i32, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> token
+  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
+  ; CHECK: llvm.intr.coro.alloc %[[CORO_ID]] : (token) -> i1
+  %shouldAlloc = call i1 @llvm.coro.alloc(token %id)
+  ret void
+}
+
 ; CHECK-LABEL:  llvm.func @coro_size()
 define void @coro_size() {
   ; CHECK: llvm.intr.coro.size : i64
@@ -881,6 +890,78 @@ define void @coro_promise(ptr %0, i32 %1, i1 %2) {
   ret void
 }
 
+; CHECK-LABEL:  llvm.func @coro_frame
+define void @coro_frame() {
+  ; CHECK: llvm.intr.coro.frame : !llvm.ptr
+  %1 = call ptr @llvm.coro.frame()
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_noop
+define void @coro_noop() {
+  ; CHECK: llvm.intr.coro.noop : !llvm.ptr
+  %1 = call ptr @llvm.coro.noop()
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_destroy
+define void @coro_destroy(ptr %0) {
+  ; CHECK: llvm.intr.coro.destroy %{{.*}}
+  call void @llvm.coro.destroy(ptr %0)
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_done
+define void @coro_done(ptr %0) {
+  ; CHECK: llvm.intr.coro.done %{{.*}} : (!llvm.ptr) -> i1
+  %2 = call i1 @llvm.coro.done(ptr %0)
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_is_in_ramp
+define void @coro_is_in_ramp() {
+  ; CHECK: llvm.intr.coro.is_in_ramp : i1
+  %1 = call i1 @llvm.coro.is_in_ramp()
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_dead
+define void @coro_dead(ptr %0) {
+  ; CHECK: llvm.intr.coro.dead %{{.*}}
+  call void @llvm.coro.dead(ptr %0)
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_await_suspend_void
+; CHECK-SAME:  %[[AWAITER:[a-zA-Z0-9]+]]
+; CHECK-SAME:  %[[HANDLE:[a-zA-Z0-9]+]]
+; CHECK-SAME:  %[[SUSPEND_FUNC:[a-zA-Z0-9]+]]
+define void @coro_await_suspend_void(ptr %awaiter, ptr %handle, ptr %suspend_func) {
+  ; CHECK: llvm.intr.coro.await.suspend.void %[[AWAITER]], %[[HANDLE]], %[[SUSPEND_FUNC]]
+  call void @llvm.coro.await.suspend.void(ptr %awaiter, ptr %handle, ptr %suspend_func)
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_await_suspend_bool
+; CHECK-SAME:  %[[AWAITER:[a-zA-Z0-9]+]]
+; CHECK-SAME:  %[[HANDLE:[a-zA-Z0-9]+]]
+; CHECK-SAME:  %[[SUSPEND_FUNC:[a-zA-Z0-9]+]]
+define void @coro_await_suspend_bool(ptr %awaiter, ptr %handle, ptr %suspend_func) {
+  ; CHECK: llvm.intr.coro.await.suspend.bool %[[AWAITER]], %[[HANDLE]], %[[SUSPEND_FUNC]] : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> i1
+  %4 = call i1 @llvm.coro.await.suspend.bool(ptr %awaiter, ptr %handle, ptr %suspend_func)
+  ret void
+}
+
+; CHECK-LABEL:  llvm.func @coro_await_suspend_handle
+; CHECK-SAME:  %[[AWAITER:[a-zA-Z0-9]+]]
+; CHECK-SAME:  %[[HANDLE:[a-zA-Z0-9]+]]
+; CHECK-SAME:  %[[SUSPEND_FUNC:[a-zA-Z0-9]+]]
+define void @coro_await_suspend_handle(ptr %awaiter, ptr %handle, ptr %suspend_func) {
+  ; CHECK: llvm.intr.coro.await.suspend.handle %[[AWAITER]], %[[HANDLE]], %[[SUSPEND_FUNC]]
+  call void @llvm.coro.await.suspend.handle(ptr %awaiter, ptr %handle, ptr %suspend_func)
+  ret void
+}
+
 ; CHECK-LABEL:  llvm.func @eh_typeid_for
 define void @eh_typeid_for(ptr %0) {
   ; CHECK: llvm.intr.eh.typeid.for %{{.*}} : (!llvm.ptr) -> i32
@@ -1406,6 +1487,7 @@ declare i16 @llvm.expect.with.probability.i16(i16, i16, double immarg)
 declare nonnull ptr @llvm.threadlocal.address.p0(ptr nonnull)
 declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
 declare ptr @llvm.coro.begin(token, ptr writeonly)
+declare i1 @llvm.coro.alloc(token)
 declare i64 @llvm.coro.size.i64()
 declare i32 @llvm.coro.size.i32()
 declare i64 @llvm.coro.align.i64()
@@ -1416,6 +1498,15 @@ declare void @llvm.coro.end(ptr, i1, token)
 declare ptr @llvm.coro.free(token, ptr nocapture readonly)
 declare void @llvm.coro.resume(ptr)
 declare ptr @llvm.coro.promise(ptr nocapture, i32, i1)
+declare ptr @llvm.coro.frame()
+declare ptr @llvm.coro.noop()
+declare void @llvm.coro.destroy(ptr)
+declare i1 @llvm.coro.done(ptr nocapture readonly)
+declare i1 @llvm.coro.is_in_ramp()
+declare void @llvm.coro.dead(ptr)
+declare void @llvm.coro.await.suspend.void(ptr, ptr, ptr)
+declare i1 @llvm.coro.await.suspend.bool(ptr, ptr, ptr)
+declare void @llvm.coro.await.suspend.handle(ptr, ptr, ptr)
 declare i32 @llvm.eh.typeid.for.p0(ptr)
 declare ptr @llvm.stacksave.p0()
 declare ptr addrspace(1) @llvm.stacksave.p1()

diff  --git a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir
index 7a362ebe2421e..d5df69a786b3e 100644
--- a/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-intrinsics.mlir
@@ -842,6 +842,17 @@ llvm.func @coro_begin(%arg0: !llvm.ptr) {
   llvm.return
 }
 
+// CHECK-LABEL: @coro_alloc
+llvm.func @coro_alloc() {
+  %zero = llvm.mlir.constant(0 : i32) : i32
+  %null = llvm.mlir.zero : !llvm.ptr
+  // CHECK: %[[ID:.*]] = call token @llvm.coro.id
+  %token = llvm.intr.coro.id %zero, %null, %null, %null : (i32, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> token
+  // CHECK: call i1 @llvm.coro.alloc(token %[[ID]])
+  %0 = llvm.intr.coro.alloc %token : (token) -> i1
+  llvm.return
+}
+
 // CHECK-LABEL: @coro_size
 llvm.func @coro_size() {
   // CHECK: call i64 @llvm.coro.size.i64
@@ -909,6 +920,84 @@ llvm.func @coro_promise(%arg0: !llvm.ptr, %arg1 : i32, %arg2 : i1) {
   llvm.return
 }
 
+// CHECK-LABEL: @coro_frame
+llvm.func @coro_frame() {
+  // CHECK: call ptr @llvm.coro.frame
+  %0 = llvm.intr.coro.frame : !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_noop
+llvm.func @coro_noop() {
+  // CHECK: call ptr @llvm.coro.noop
+  %0 = llvm.intr.coro.noop : !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_destroy
+llvm.func @coro_destroy(%arg0: !llvm.ptr) {
+  // CHECK: call void @llvm.coro.destroy
+  llvm.intr.coro.destroy %arg0 : !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_done
+llvm.func @coro_done(%arg0: !llvm.ptr) {
+  // CHECK: call i1 @llvm.coro.done
+  %0 = llvm.intr.coro.done %arg0 : (!llvm.ptr) -> i1
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_is_in_ramp
+llvm.func @coro_is_in_ramp() {
+  // CHECK: call i1 @llvm.coro.is_in_ramp
+  %0 = llvm.intr.coro.is_in_ramp : i1
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_dead
+llvm.func @coro_dead(%arg0: !llvm.ptr) {
+  // CHECK: call void @llvm.coro.dead
+  llvm.intr.coro.dead %arg0 : !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_await_suspend_void
+// CHECK-SAME:  ptr %[[AWAITER:[a-zA-Z0-9]+]]
+// CHECK-SAME:  ptr %[[HANDLE:[a-zA-Z0-9]+]]
+// CHECK-SAME:  ptr %[[SUSPEND_FUNC:[a-zA-Z0-9]+]]
+llvm.func @coro_await_suspend_void(%arg0: !llvm.ptr, %arg1: !llvm.ptr,
+                                   %arg2: !llvm.ptr) {
+  // CHECK: call void @llvm.coro.await.suspend.void(ptr %[[AWAITER]], ptr %[[HANDLE]], ptr %[[SUSPEND_FUNC]])
+  llvm.intr.coro.await.suspend.void %arg0, %arg1, %arg2
+    : !llvm.ptr, !llvm.ptr, !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_await_suspend_bool
+// CHECK-SAME:  ptr %[[AWAITER:[a-zA-Z0-9]+]]
+// CHECK-SAME:  ptr %[[HANDLE:[a-zA-Z0-9]+]]
+// CHECK-SAME:  ptr %[[SUSPEND_FUNC:[a-zA-Z0-9]+]]
+llvm.func @coro_await_suspend_bool(%arg0: !llvm.ptr, %arg1: !llvm.ptr,
+                                   %arg2: !llvm.ptr) {
+  // CHECK: call i1 @llvm.coro.await.suspend.bool(ptr %[[AWAITER]], ptr %[[HANDLE]], ptr %[[SUSPEND_FUNC]])
+  %0 = llvm.intr.coro.await.suspend.bool %arg0, %arg1, %arg2
+    : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> i1
+  llvm.return
+}
+
+// CHECK-LABEL: @coro_await_suspend_handle
+// CHECK-SAME:  ptr %[[AWAITER:[a-zA-Z0-9]+]]
+// CHECK-SAME:  ptr %[[HANDLE:[a-zA-Z0-9]+]]
+// CHECK-SAME:  ptr %[[SUSPEND_FUNC:[a-zA-Z0-9]+]]
+llvm.func @coro_await_suspend_handle(%arg0: !llvm.ptr, %arg1: !llvm.ptr,
+                                     %arg2: !llvm.ptr) {
+  // CHECK: call void @llvm.coro.await.suspend.handle(ptr %[[AWAITER]], ptr %[[HANDLE]], ptr %[[SUSPEND_FUNC]])
+  llvm.intr.coro.await.suspend.handle %arg0, %arg1, %arg2
+    : !llvm.ptr, !llvm.ptr, !llvm.ptr
+  llvm.return
+}
+
 // CHECK-LABEL: @eh_typeid_for
 llvm.func @eh_typeid_for(%arg0 : !llvm.ptr) {
     // CHECK: call i32 @llvm.eh.typeid.for.p0
@@ -1526,6 +1615,7 @@ llvm.func @vector_scmp(%a: vector<4 x i32>, %b: vector<4 x i32>) -> vector<4 x i
 // CHECK-DAG: declare nonnull ptr @llvm.threadlocal.address.p0(ptr nonnull)
 // CHECK-DAG: declare token @llvm.coro.id(i32, ptr readnone, ptr readonly captures(none), ptr)
 // CHECK-DAG: declare ptr @llvm.coro.begin(token, ptr writeonly)
+// CHECK-DAG: declare i1 @llvm.coro.alloc(token)
 // CHECK-DAG: declare i64 @llvm.coro.size.i64()
 // CHECK-DAG: declare i32 @llvm.coro.size.i32()
 // CHECK-DAG: declare token @llvm.coro.save(ptr)
@@ -1534,6 +1624,15 @@ llvm.func @vector_scmp(%a: vector<4 x i32>, %b: vector<4 x i32>) -> vector<4 x i
 // CHECK-DAG: declare ptr @llvm.coro.free(token, ptr readonly captures(none))
 // CHECK-DAG: declare void @llvm.coro.resume(ptr)
 // CHECK-DAG: declare ptr @llvm.coro.promise(ptr captures(none), i32, i1)
+// CHECK-DAG: declare ptr @llvm.coro.frame()
+// CHECK-DAG: declare ptr @llvm.coro.noop()
+// CHECK-DAG: declare void @llvm.coro.destroy(ptr)
+// CHECK-DAG: declare i1 @llvm.coro.done(ptr readonly captures(none))
+// CHECK-DAG: declare i1 @llvm.coro.is_in_ramp()
+// CHECK-DAG: declare void @llvm.coro.dead(ptr)
+// CHECK-DAG: declare void @llvm.coro.await.suspend.void(ptr, ptr, ptr)
+// CHECK-DAG: declare i1 @llvm.coro.await.suspend.bool(ptr, ptr, ptr)
+// CHECK-DAG: declare void @llvm.coro.await.suspend.handle(ptr, ptr, ptr)
 // CHECK-DAG: declare <8 x i32> @llvm.vp.add.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32)
 // CHECK-DAG: declare <8 x i32> @llvm.vp.sub.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32)
 // CHECK-DAG: declare <8 x i32> @llvm.vp.mul.v8i32(<8 x i32>, <8 x i32>, <8 x i1>, i32)


        


More information about the Mlir-commits mailing list