Merge pull request #8108 from google/benvanik-vm-yield

Preparing for VM coroutine yields via `vm.yield`.
diff --git a/iree/base/status.c b/iree/base/status.c
index 8fe228e..ceebea4 100644
--- a/iree/base/status.c
+++ b/iree/base/status.c
@@ -220,8 +220,6 @@
       return "ALREADY_EXISTS";
     case IREE_STATUS_PERMISSION_DENIED:
       return "PERMISSION_DENIED";
-    case IREE_STATUS_UNAUTHENTICATED:
-      return "UNAUTHENTICATED";
     case IREE_STATUS_RESOURCE_EXHAUSTED:
       return "RESOURCE_EXHAUSTED";
     case IREE_STATUS_FAILED_PRECONDITION:
@@ -238,6 +236,10 @@
       return "UNAVAILABLE";
     case IREE_STATUS_DATA_LOSS:
       return "DATA_LOSS";
+    case IREE_STATUS_UNAUTHENTICATED:
+      return "UNAUTHENTICATED";
+    case IREE_STATUS_DEFERRED:
+      return "DEFERRED";
     default:
       return "";
   }
diff --git a/iree/base/status.h b/iree/base/status.h
index a26352c..7192069 100644
--- a/iree/base/status.h
+++ b/iree/base/status.h
@@ -73,24 +73,85 @@
 // Note that any code within IREE_STATUS_CODE_MASK is valid even if not
 // enumerated here. Always check for unhandled errors/have default conditions.
 typedef enum iree_status_code_e {
+  // Successful operation.
   IREE_STATUS_OK = 0,
+
+  // Operation was cancelled by the caller.
   IREE_STATUS_CANCELLED = 1,
+
+  // Unknown error, or error that could not be mapped to this enum.
   IREE_STATUS_UNKNOWN = 2,
+
+  // The caller provided an invalid argument and that future calls with the same
+  // arguments will fail. If the failure is predicated on system state that may
+  // change prefer IREE_STATUS_OUT_OF_RANGE.
   IREE_STATUS_INVALID_ARGUMENT = 3,
+
+  // A deadline was exceeded before the call could complete.
+  // This can be returned even if the operation would have completed
+  // successfully had the deadline not been met.
   IREE_STATUS_DEADLINE_EXCEEDED = 4,
+
+  // A referenced resource could not be found or was unavailable to all
+  // requesters. IREE_STATUS_PERMISSION_DENIED should be used if only an
+  // individual requester is denied access.
   IREE_STATUS_NOT_FOUND = 5,
+
+  // The resource the caller attempted to create already exists.
   IREE_STATUS_ALREADY_EXISTS = 6,
+
+  // The caller does not have permission to execute the operation or have access
+  // to the requested resources.
   IREE_STATUS_PERMISSION_DENIED = 7,
+
+  // Some resource type has been exhausted and the operation is unable to
+  // reserve what it requires, either by quota or underlying system exhaustion.
   IREE_STATUS_RESOURCE_EXHAUSTED = 8,
+
+  // The operation was rejected because the system is not in a state required
+  // for the operation's execution.
+  //
+  // Use IREE_STATUS_UNAVAILABLE if the caller can retry the operation.
+  // Use IREE_STATUS_ABORTED if the caller should restart their transaction
+  // (the entire sequence of operations is invalid).
+  // Use IREE_STATUS_FAILED_PRECONDITION if the caller should not retry until
+  // the system state has been explicitly fixed.
   IREE_STATUS_FAILED_PRECONDITION = 9,
+
+  // The operation was aborted by the system.
+  // If responding to a caller-requested cancellation use IREE_STATUS_CANCELLED.
   IREE_STATUS_ABORTED = 10,
+
+  // The operation was attempted past the valid range (of a resource, etc).
+  // Indicates the operation can be retried if the system state is fixed.
   IREE_STATUS_OUT_OF_RANGE = 11,
+
+  // Operation has not been implemented or is not supported.
   IREE_STATUS_UNIMPLEMENTED = 12,
+
+  // An internal error has occurred and some invariants expected by an
+  // underlying system have been violated. This error code is reserved for
+  // serious errors.
   IREE_STATUS_INTERNAL = 13,
+
+  // The system used to perform the operation is currently (and transiently)
+  // unavailable. Callers can retry with backoff.
   IREE_STATUS_UNAVAILABLE = 14,
+
+  // An serious unrecoverable data loss or corruption has occurred.
+  // Indicates that an underlying system or resource has failed in such a way
+  // that all related operations may produce incorrect results.
   IREE_STATUS_DATA_LOSS = 15,
+
+  // The requested operation does not have proper authentication.
+  // Callers can correct this and retry.
   IREE_STATUS_UNAUTHENTICATED = 16,
 
+  // The operation has been deferred and must be resumed at a future point.
+  // Used by resumable operations as part of scheduling and execution systems.
+  // Callers that do not handle deferred execution can treat this as a failure.
+  IREE_STATUS_DEFERRED = 17,
+
   IREE_STATUS_CODE_MASK = 0x1Fu,
 } iree_status_code_t;
 
@@ -151,6 +212,8 @@
   (iree_status_code(value) == IREE_STATUS_DATA_LOSS)
 #define iree_status_is_unauthenticated(value) \
   (iree_status_code(value) == IREE_STATUS_UNAUTHENTICATED)
+#define iree_status_is_deferred(value) \
+  (iree_status_code(value) == IREE_STATUS_DEFERRED)
 
 #define IREE_STATUS_IMPL_CONCAT_INNER_(x, y) x##y
 #define IREE_STATUS_IMPL_CONCAT_(x, y) IREE_STATUS_IMPL_CONCAT_INNER_(x, y)
diff --git a/iree/base/status_cc.h b/iree/base/status_cc.h
index 21fad56..4795dda 100644
--- a/iree/base/status_cc.h
+++ b/iree/base/status_cc.h
@@ -88,6 +88,7 @@
   kUnavailable = IREE_STATUS_UNAVAILABLE,
   kDataLoss = IREE_STATUS_DATA_LOSS,
   kUnauthenticated = IREE_STATUS_UNAUTHENTICATED,
+  kDeferred = IREE_STATUS_DEFERRED,
 };
 
 static inline const char* StatusCodeToString(StatusCode code) {
diff --git a/iree/compiler/Dialect/Util/IR/UtilTypes.h b/iree/compiler/Dialect/Util/IR/UtilTypes.h
index 2d47d4f..6527a37 100644
--- a/iree/compiler/Dialect/Util/IR/UtilTypes.h
+++ b/iree/compiler/Dialect/Util/IR/UtilTypes.h
@@ -59,6 +59,7 @@
   Unavailable = 14,
   DataLoss = 15,
   Unauthenticated = 16,
+  Deferred = 17,
   DoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20
 };
 
diff --git a/iree/compiler/Dialect/VM/IR/VMOps.cpp b/iree/compiler/Dialect/VM/IR/VMOps.cpp
index e4008de..ee5e558 100644
--- a/iree/compiler/Dialect/VM/IR/VMOps.cpp
+++ b/iree/compiler/Dialect/VM/IR/VMOps.cpp
@@ -1151,6 +1151,22 @@
 // Async/fiber ops
 //===----------------------------------------------------------------------===//
 
+Block *YieldOp::getDest() { return getOperation()->getSuccessor(0); }
+
+void YieldOp::setDest(Block *block) {
+  return getOperation()->setSuccessor(block, 0);
+}
+
+void YieldOp::eraseOperand(unsigned index) {
+  getOperation()->eraseOperand(index);
+}
+
+Optional<MutableOperandRange> YieldOp::getMutableSuccessorOperands(
+    unsigned index) {
+  assert(index == 0 && "invalid successor index");
+  return destOperandsMutable();
+}
+
 //===----------------------------------------------------------------------===//
 // Debugging
 //===----------------------------------------------------------------------===//
diff --git a/iree/compiler/Dialect/VM/IR/VMOps.td b/iree/compiler/Dialect/VM/IR/VMOps.td
index eecb75d..5ce09bb 100644
--- a/iree/compiler/Dialect/VM/IR/VMOps.td
+++ b/iree/compiler/Dialect/VM/IR/VMOps.td
@@ -3739,21 +3739,57 @@
 //   await_any
 
 def VM_YieldOp : VM_Op<"yield", [
+    DeclareOpInterfaceMethods<BranchOpInterface>,
     DeclareOpInterfaceMethods<VM_SerializableOpInterface>,
     HasParent<"IREE::VM::FuncOp">,
+    Terminator,
     YieldPoint,
   ]> {
   let summary = [{unconditional fiber yield operation}];
   let description = [{
     Yields the fiber for some (likely short) amount of time. This can be used to
-    perform cooperative scheduling and ensure fair (enough) execution.
+    perform cooperative scheduling and ensure fair (enough) execution. Execution
+    resumes at the specified target branch.
+
+    ```
+    ^bb0:
+      vm.yield ^on_resume
+    ^on_resume:
+      ...
+   ```
   }];
 
-  let assemblyFormat = "attr-dict";
+  let arguments = (ins
+    Variadic<VM_AnyType>:$destOperands
+  );
+
+  let successors = (successor
+    AnySuccessor:$dest
+  );
+
+  let assemblyFormat = [{
+    $dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict
+  }];
 
   let encoding = [
     VM_EncOpcode<VM_OPC_Yield>,
+    VM_EncBranch<"dest", "getOperands", 0>,
   ];
+
+  let builders = [
+    OpBuilder<(ins "Block *":$dest, CArg<"ValueRange", "{}">:$destOperands), [{
+      $_state.addSuccessors(dest);
+      $_state.addOperands(destOperands);
+    }]>,
+  ];
+
+  let extraClassDeclaration = [{
+    Block *getDest();
+    void setDest(Block *block);
+
+    /// Erase the operand at 'index' from the operand list.
+    void eraseOperand(unsigned index);
+  }];
 }
 
 //===----------------------------------------------------------------------===//
@@ -3851,11 +3887,11 @@
   ];
 
   let builders = [
-    OpBuilder<(ins "Block *":$dest, CArg<"ValueRange", "{}">:$destOperands),
-    [{
+    OpBuilder<(ins "Block *":$dest, CArg<"ValueRange", "{}">:$destOperands), [{
       $_state.addSuccessors(dest);
       $_state.addOperands(destOperands);
-  }]>];
+    }]>,
+  ];
 
   let extraClassDeclaration = [{
     Block *getDest();
diff --git a/iree/compiler/Dialect/VM/IR/test/control_flow_ops.mlir b/iree/compiler/Dialect/VM/IR/test/control_flow_ops.mlir
index beff9a6..4281968 100644
--- a/iree/compiler/Dialect/VM/IR/test/control_flow_ops.mlir
+++ b/iree/compiler/Dialect/VM/IR/test/control_flow_ops.mlir
@@ -217,8 +217,9 @@
 // CHECK-LABEL: @yield
 vm.module @my_module {
   vm.func @yield() {
-    // CHECK: vm.yield
-    vm.yield
+    // CHECK: vm.yield ^bb1
+    vm.yield ^bb1
+  ^bb1:
     vm.return
   }
 }
diff --git a/iree/vm/bytecode_disasm.c b/iree/vm/bytecode_disasm.c
index 2dbeed0..08400a0 100644
--- a/iree/vm/bytecode_disasm.c
+++ b/iree/vm/bytecode_disasm.c
@@ -1363,8 +1363,13 @@
     //===------------------------------------------------------------------===//
 
     DISASM_OP(CORE, Yield) {
+      int32_t block_pc = VM_DecBranchTarget("dest");
+      const iree_vm_register_remap_list_t* remap_list =
+          VM_ParseBranchOperands("operands");
       IREE_RETURN_IF_ERROR(
-          iree_string_builder_append_cstring(b, "vm.yield (TBD)"));
+          iree_string_builder_append_format(b, "vm.yield ^%08X(", block_pc));
+      EMIT_REMAP_LIST(remap_list);
+      IREE_RETURN_IF_ERROR(iree_string_builder_append_cstring(b, ")"));
       break;
     }
 
diff --git a/iree/vm/bytecode_dispatch.c b/iree/vm/bytecode_dispatch.c
index 23c7647..73e3a77 100644
--- a/iree/vm/bytecode_dispatch.c
+++ b/iree/vm/bytecode_dispatch.c
@@ -1486,8 +1486,18 @@
     //===------------------------------------------------------------------===//
 
     DISPATCH_OP(CORE, Yield, {
-      // TODO(benvanik): yield with execution results.
-      return iree_ok_status();
+      // Perform branch before yielding; in this way we will resume at the
+      // target without needing to retain any information about the yield.
+      int32_t block_pc = VM_DecBranchTarget("dest");
+      const iree_vm_register_remap_list_t* remap_list =
+          VM_DecBranchOperands("operands");
+      iree_vm_bytecode_dispatch_remap_branch_registers(regs, remap_list);
+      pc = block_pc;
+
+      // Return magic status code indicating a yield.
+      // This isn't an error, though callers not supporting coroutines will
+      // treat it as one and propagate it up.
+      return iree_status_from_code(IREE_STATUS_DEFERRED);
     });
 
     //===------------------------------------------------------------------===//