diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td @@ -52,6 +52,12 @@ CPred<"::mlir::LLVM::isCompatibleType($_self)">, "LLVM dialect-compatible type">; +// Type constraint accepting LLVM token type. +def LLVM_TokenType : Type< + CPred<"$_self.isa<::mlir::LLVM::LLVMTokenType>()">, + "LLVM token type">, + BuildableType<"::mlir::LLVM::LLVMTokenType::get($_builder.getContext())">; + // Type constraint accepting LLVM integer types. def LLVM_AnyInteger : Type< CPred<"$_self.isa<::mlir::IntegerType>()">, @@ -89,6 +95,18 @@ def LLVM_AnyPointer : Type()">, "LLVM pointer type">; +// Type constraint accepting LLVM i8* pointer type (opaque poointers in LLVM +// are represented as i8 pointers). +def LLVM_OpaquePointerType : Type< + And<[LLVM_AnyPointer.predicate, + SubstLeaves< + "$_self", + "$_self.cast<::mlir::LLVM::LLVMPointerType>().getElementType()", + LLVM_i8.predicate>]>, + "LLVM opaque pointer type">, + BuildableType<"::mlir::LLVM::LLVMPointerType::get(" + "::mlir::IntegerType::get($_builder.getContext(), 8))">; + // Type constraint accepting LLVM pointer type with an additional constraint // on the element type. class LLVM_PointerTo : Type< diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1226,6 +1226,57 @@ let arguments = (ins LLVM_Type, LLVM_Type); } +// +// Coroutine intrinsics. +// + +def LLVM_CoroIdOp : LLVM_IntrOp<"coro.id", [], [], [], 1> { + let arguments = (ins LLVM_i32:$align, + LLVM_OpaquePointerType:$promise, + LLVM_OpaquePointerType:$coroaddr, + LLVM_OpaquePointerType:$fnaddrs); + let assemblyFormat = "$align `,` $promise `,` $coroaddr `,` $fnaddrs" + " attr-dict `:` type($res)"; +} + +def LLVM_CoroBeginOp : LLVM_IntrOp<"coro.begin", [], [], [], 1> { + let arguments = (ins LLVM_TokenType:$token, + LLVM_OpaquePointerType:$mem); + let assemblyFormat = "$token `,` $mem attr-dict `:` type($res)"; +} + +def LLVM_CoroSizeOp : LLVM_IntrOp<"coro.size", [0], [], [], 1> { + let assemblyFormat = "attr-dict `:` type($res)"; +} + +def LLVM_CoroSaveOp : LLVM_IntrOp<"coro.save", [], [], [], 1> { + let arguments = (ins LLVM_OpaquePointerType:$handle); + let assemblyFormat = "$handle attr-dict `:` type($res)"; +} + +def LLVM_CoroSuspendOp : LLVM_IntrOp<"coro.suspend", [], [], [], 1> { + let arguments = (ins LLVM_TokenType:$save, + LLVM_i1:$final); + let assemblyFormat = "$save `,` $final attr-dict `:` type($res)"; +} + +def LLVM_CoroEndOp : LLVM_IntrOp<"coro.end", [], [], [], 1> { + let arguments = (ins LLVM_OpaquePointerType:$handle, + LLVM_i1:$unwind); + let assemblyFormat = "$handle `,` $unwind attr-dict `:` type($res)"; +} + +def LLVM_CoroFreeOp : LLVM_IntrOp<"coro.free", [], [], [], 1> { + let arguments = (ins LLVM_TokenType:$id, + LLVM_OpaquePointerType:$handle); + let assemblyFormat = "$id `,` $handle attr-dict `:` type($res)"; +} + +def LLVM_CoroResumeOp : LLVM_IntrOp<"coro.resume", [], [], [], 0> { + let arguments = (ins LLVM_OpaquePointerType:$handle); + let assemblyFormat = "$handle attr-dict"; +} + // // Vector Reductions. // diff --git a/mlir/test/Target/llvmir-intrinsics.mlir b/mlir/test/Target/llvmir-intrinsics.mlir --- a/mlir/test/Target/llvmir-intrinsics.mlir +++ b/mlir/test/Target/llvmir-intrinsics.mlir @@ -347,6 +347,64 @@ llvm.return } +// CHECK-LABEL: @coro_id +llvm.func @coro_id(%arg0: i32, %arg1: !llvm.ptr) { + // CHECK: call token @llvm.coro.id + llvm.intr.coro.id %arg0, %arg1, %arg1, %arg1 : !llvm.token + llvm.return +} + +// CHECK-LABEL: @coro_begin +llvm.func @coro_begin(%arg0: !llvm.token, %arg1: !llvm.ptr) { + // CHECK: call i8* @llvm.coro.begin + llvm.intr.coro.begin %arg0, %arg1 : !llvm.ptr + llvm.return +} + +// CHECK-LABEL: @coro_size +llvm.func @coro_size() { + // CHECK: call i64 @llvm.coro.size.i64 + %0 = llvm.intr.coro.size : i64 + // CHECK: call i32 @llvm.coro.size.i32 + %1 = llvm.intr.coro.size : i32 + llvm.return +} + +// CHECK-LABEL: @coro_save +llvm.func @coro_save(%arg0: !llvm.ptr) { + // CHECK: call token @llvm.coro.save + %0 = llvm.intr.coro.save %arg0 : !llvm.token + llvm.return +} + +// CHECK-LABEL: @coro_suspend +llvm.func @coro_suspend(%arg0: !llvm.token, %arg1 : i1) { + // CHECK: call i8 @llvm.coro.suspend + %0 = llvm.intr.coro.suspend %arg0, %arg1 : !llvm.i8 + llvm.return +} + +// CHECK-LABEL: @coro_end +llvm.func @coro_end(%arg0: !llvm.ptr, %arg1 : i1) { + // CHECK: call i1 @llvm.coro.end + %0 = llvm.intr.coro.end %arg0, %arg1 : !llvm.i1 + llvm.return +} + +// CHECK-LABEL: @coro_free +llvm.func @coro_free(%arg0: !llvm.token, %arg1 : !llvm.ptr) { + // CHECK: call i8* @llvm.coro.free + %0 = llvm.intr.coro.free %arg0, %arg1 : !llvm.ptr + llvm.return +} + +// CHECK-LABEL: @coro_resume +llvm.func @coro_resume(%arg0: !llvm.ptr) { + // CHECK: call void @llvm.coro.resume + llvm.intr.coro.resume %arg0 + llvm.return +} + // Check that intrinsics are declared with appropriate types. // CHECK-DAG: declare float @llvm.fma.f32(float, float, float) // CHECK-DAG: declare <8 x float> @llvm.fma.v8f32(<8 x float>, <8 x float>, <8 x float>) #0 @@ -393,3 +451,12 @@ // CHECK-DAG: declare { <8 x i32>, <8 x i1> } @llvm.usub.with.overflow.v8i32(<8 x i32>, <8 x i32>) #0 // CHECK-DAG: declare { i32, i1 } @llvm.umul.with.overflow.i32(i32, i32) // CHECK-DAG: declare { <8 x i32>, <8 x i1> } @llvm.umul.with.overflow.v8i32(<8 x i32>, <8 x i32>) #0 +// CHECK-DAG: declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) +// CHECK-DAG: declare i8* @llvm.coro.begin(token, i8* writeonly) +// CHECK-DAG: declare i64 @llvm.coro.size.i64() +// CHECK-DAG: declare i32 @llvm.coro.size.i32() +// CHECK-DAG: declare token @llvm.coro.save(i8*) +// CHECK-DAG: declare i8 @llvm.coro.suspend(token, i1) +// CHECK-DAG: declare i1 @llvm.coro.end(i8*, i1) +// CHECK-DAG: declare i8* @llvm.coro.free(token, i8* nocapture readonly) +// CHECK-DAG: declare void @llvm.coro.resume(i8*)