@@ -14,7 +14,10 @@ include "circt/Dialect/LLHD/LLHDTypes.td"
1414include "mlir/IR/EnumAttr.td"
1515include "mlir/IR/OpAsmInterface.td"
1616include "mlir/IR/SymbolInterfaces.td"
17+ include "mlir/IR/RegionKindInterface.td"
18+ include "mlir/Interfaces/CallInterfaces.td"
1719include "mlir/Interfaces/ControlFlowInterfaces.td"
20+ include "mlir/Interfaces/FunctionInterfaces.td"
1821include "mlir/Interfaces/InferTypeOpInterface.td"
1922include "mlir/Interfaces/MemorySlotInterfaces.td"
2023include "mlir/Interfaces/SideEffectInterfaces.td"
@@ -628,10 +631,10 @@ def CombinationalOp : LLHDOp<"combinational", [
628631
629632def WaitOp : LLHDOp<"wait", [
630633 AttrSizedOperandSegments,
631- HasParent< "ProcessOp">,
634+ ParentOneOf<[ "ProcessOp", "CoroutineOp"] >,
632635 Terminator,
633636]> {
634- let summary = "Suspend execution of a process";
637+ let summary = "Suspend execution of a process or coroutine ";
635638 let description = [{
636639 The `llhd.wait` terminator suspends execution of the parent process until
637640 any of the `observed` values change or a fixed `delay` has passed. Execution
@@ -664,10 +667,10 @@ def WaitOp : LLHDOp<"wait", [
664667}
665668
666669def HaltOp : LLHDOp<"halt", [
667- ParentOneOf<["ProcessOp", "FinalOp"]>,
670+ ParentOneOf<["ProcessOp", "FinalOp", "CoroutineOp" ]>,
668671 Terminator,
669672]> {
670- let summary = "Terminate execution of a process";
673+ let summary = "Terminate execution of a process or coroutine ";
671674 let description = [{
672675 The `llhd.halt` terminator suspends execution of the parent process forever,
673676 effectively terminating it. The `yieldOperands` are yielded as the result
@@ -718,4 +721,172 @@ def YieldOp : LLHDOp<"yield", [
718721 let hasVerifier = 1;
719722}
720723
724+ //===----------------------------------------------------------------------===//
725+ // Coroutines
726+ //===----------------------------------------------------------------------===//
727+
728+ def CoroutineOp : LLHDOp<"coroutine", [
729+ IsolatedFromAbove,
730+ FunctionOpInterface,
731+ Symbol,
732+ RegionKindInterface,
733+ ProceduralRegion,
734+ RecursiveMemoryEffects,
735+ ]> {
736+ let summary = "Define a coroutine";
737+ let description = [{
738+ The `llhd.coroutine` op defines a suspendable subroutine that can be called
739+ from processes or other coroutines. It represents the lowered form of a
740+ SystemVerilog task. Unlike regular functions, coroutines can suspend
741+ execution using `llhd.wait` and terminate using `llhd.halt`, just like
742+ processes. Calling a coroutine via `llhd.call_coroutine` suspends the caller
743+ until the coroutine returns.
744+
745+ Example:
746+ ```mlir
747+ llhd.coroutine @waitForClk(%clk: !llhd.ref<i1>) {
748+ %0 = llhd.prb %clk : !llhd.ref<i1>
749+ llhd.wait (%clk : !llhd.ref<i1>), ^resume
750+ ^resume:
751+ llhd.return
752+ }
753+ ```
754+ }];
755+
756+ let arguments = (ins
757+ SymbolNameAttr:$sym_name,
758+ TypeAttrOf<FunctionType>:$function_type,
759+ OptionalAttr<StrAttr>:$sym_visibility,
760+ OptionalAttr<DictArrayAttr>:$arg_attrs,
761+ OptionalAttr<DictArrayAttr>:$res_attrs
762+ );
763+ let results = (outs);
764+ let regions = (region MinSizedRegion<1>:$body);
765+
766+ let hasCustomAssemblyFormat = 1;
767+
768+ let builders = [
769+ OpBuilder<(ins "mlir::StringAttr":$sym_name,
770+ "mlir::TypeAttr":$function_type), [{
771+ build($_builder, $_state, sym_name, function_type,
772+ /*sym_visibility=*/mlir::StringAttr(),
773+ /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr);
774+ }]>,
775+ OpBuilder<(ins "mlir::StringRef":$sym_name,
776+ "mlir::FunctionType":$function_type), [{
777+ build($_builder, $_state,
778+ $_builder.getStringAttr(sym_name),
779+ mlir::TypeAttr::get(function_type),
780+ /*sym_visibility=*/mlir::StringAttr(),
781+ /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr);
782+ }]>,
783+ ];
784+
785+ let extraClassDeclaration = [{
786+ static mlir::RegionKind getRegionKind(unsigned index) {
787+ return mlir::RegionKind::SSACFG;
788+ }
789+
790+ /// Returns the argument types of this coroutine.
791+ mlir::ArrayRef<mlir::Type> getArgumentTypes() {
792+ return getFunctionType().getInputs();
793+ }
794+
795+ /// Returns the result types of this coroutine.
796+ mlir::ArrayRef<mlir::Type> getResultTypes() {
797+ return getFunctionType().getResults();
798+ }
799+
800+ //===------------------------------------------------------------------===//
801+ // CallableOpInterface
802+ //===------------------------------------------------------------------===//
803+
804+ mlir::Region *getCallableRegion() { return &getBody(); }
805+ }];
806+ }
807+
808+ def CallCoroutineOp : LLHDOp<"call_coroutine", [
809+ CallOpInterface,
810+ DeclareOpInterfaceMethods<SymbolUserOpInterface>,
811+ ]> {
812+ let summary = "Call a coroutine";
813+ let description = [{
814+ The `llhd.call_coroutine` op calls an `llhd.coroutine`. The calling process
815+ or coroutine suspends until the callee returns. This is only valid inside a
816+ process or another coroutine.
817+
818+ Example:
819+ ```mlir
820+ llhd.call_coroutine @waitForClk(%clk) : (!llhd.ref<i1>) -> ()
821+ ```
822+ }];
823+
824+ let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$operands);
825+ let results = (outs Variadic<AnyType>);
826+
827+ let assemblyFormat = [{
828+ $callee `(` $operands `)` attr-dict `:` functional-type(operands, results)
829+ }];
830+
831+ let builders = [
832+ OpBuilder<(ins "CoroutineOp":$coroutine,
833+ CArg<"mlir::ValueRange", "{}">:$operands), [{
834+ build($_builder, $_state, coroutine.getFunctionType().getResults(),
835+ mlir::SymbolRefAttr::get(coroutine), operands);
836+ }]>,
837+ ];
838+
839+ let extraClassDeclaration = [{
840+ operand_range getArgOperands() {
841+ return getOperands();
842+ }
843+ MutableOperandRange getArgOperandsMutable() {
844+ return getOperandsMutable();
845+ }
846+
847+ mlir::CallInterfaceCallable getCallableForCallee() {
848+ return (*this)->getAttrOfType<mlir::SymbolRefAttr>("callee");
849+ }
850+
851+ void setCalleeFromCallable(mlir::CallInterfaceCallable callee) {
852+ (*this)->setAttr(getCalleeAttrName(),
853+ llvm::cast<mlir::SymbolRefAttr>(callee));
854+ }
855+
856+ /// CallOpInterface requires ArgAndResultAttrsOpInterface, which needs
857+ /// methods to get/set per-argument and per-result attributes. Call sites
858+ /// don't carry these attributes, so we stub them out as no-ops.
859+ mlir::ArrayAttr getArgAttrsAttr() { return nullptr; }
860+ mlir::ArrayAttr getResAttrsAttr() { return nullptr; }
861+ void setArgAttrsAttr(mlir::ArrayAttr args) {}
862+ void setResAttrsAttr(mlir::ArrayAttr args) {}
863+ mlir::Attribute removeArgAttrsAttr() { return nullptr; }
864+ mlir::Attribute removeResAttrsAttr() { return nullptr; }
865+ }];
866+ }
867+
868+ def ReturnOp : LLHDOp<"return", [
869+ HasParent<"CoroutineOp">,
870+ Terminator,
871+ ]> {
872+ let summary = "Return from a coroutine";
873+ let description = [{
874+ The `llhd.return` op terminates execution of the enclosing
875+ `llhd.coroutine` and returns control to the caller. It is the coroutine
876+ equivalent of `llhd.halt` for processes.
877+
878+ Example:
879+ ```mlir
880+ llhd.coroutine @myTask() {
881+ llhd.return
882+ }
883+ ```
884+ }];
885+ let arguments = (ins Variadic<AnyType>:$operands);
886+ let assemblyFormat = [{
887+ ($operands^ `:` type($operands))?
888+ attr-dict
889+ }];
890+ }
891+
721892#endif // CIRCT_DIALECT_LLHD_LLHDOPS_TD
0 commit comments