Optimize Empty -> Failing state transition

We can avoid promotion of Empty to NodeList, but create new empty
NodeList and capture it in the new Failing instance
diff --git a/common/kotlinx-coroutines-core-common/src/JobSupport.kt b/common/kotlinx-coroutines-core-common/src/JobSupport.kt
index 609ea81..9c55e3f 100644
--- a/common/kotlinx-coroutines-core-common/src/JobSupport.kt
+++ b/common/kotlinx-coroutines-core-common/src/JobSupport.kt
@@ -690,11 +690,10 @@
                 }
                 is Incomplete -> {
                     // Not yet finishing -- try to make it failing
-                    val list = tryPromoteToList(state) ?: return@loopOnState
                     val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
                     if (state.isActive) {
                         // active state becomes failing
-                        if (tryMakeFailing(state, list, causeException, cancel)) return true
+                        if (tryMakeFailing(state, causeException, cancel)) return true
                     } else {
                         // non active state starts completing
                         when (tryMakeCompleting(state, createFailure(causeException, cancel), mode = MODE_ATOMIC_DEFAULT)) {
@@ -710,19 +709,26 @@
         }
     }
 
-    // Performs promotion of incomplete coroutine state to NodeList, returns null when need to retry
-    private fun tryPromoteToList(state: Incomplete): NodeList? = state.list ?: null.also {
+    // Performs promotion of incomplete coroutine state to NodeList for the purpose of
+    // converting coroutine state to Failing, returns null when need to retry
+    private fun getOrPromoteFailingList(state: Incomplete): NodeList? = state.list ?: 
         when (state) {
-            is Empty -> promoteEmptyToNodeList(state)
-            is JobNode<*> -> promoteSingleToNodeList(state)
+            is Empty -> NodeList() // we can allocate new empty list that'll get integrated into Failing state
+            is JobNode<*> -> {
+                // SINGLE/SINGLE+ must be promoted to NodeList first, because otherwise we cannot
+                // correctly capture a reference to it
+                promoteSingleToNodeList(state)
+                null // retry
+            }
             else -> error("State should have list: $state")
         }
-    }
 
     // try make new failing state on the condition that we're still in the expected state
-    private fun tryMakeFailing(state: Incomplete, list: NodeList, rootCause: Throwable, cancel: Boolean): Boolean {
+    private fun tryMakeFailing(state: Incomplete, rootCause: Throwable, cancel: Boolean): Boolean {
         check(state !is Finishing) // only for non-finishing states
         check(state.isActive) // only for active states
+        // get state's list or else promote to list to correctly operate on child lists
+        val list = getOrPromoteFailingList(state) ?: return false
         // Create failing state (with rootCause!)
         val failing = Finishing(list, cancel, false, rootCause)
         if (!_state.compareAndSet(state, failing)) return false
@@ -782,7 +788,7 @@
             return COMPLETING_COMPLETED
         }
         // get state's list or else promote to list to correctly operate on child lists
-        val list = tryPromoteToList(state) ?: return COMPLETING_RETRY
+        val list = getOrPromoteFailingList(state) ?: return COMPLETING_RETRY
         // promote to Finishing state if we are not in it yet
         // This promotion has to be atomic w.r.t to state change, so that a coroutine that is not active yet
         // atomically transition to finishing & completing state