Basically, you can't treat green threads just like "a multi-threaded runtime" and have it just work. That is, a 1:1 mapping between green threads and OS threads is just OS threads.
So fundamentally if you bounce your green stacks off of the actual stack they're going to need to go somewhere... and that place must be the heap.
There are pluses and minuses to this implementation, but the biggest minus is that it makes FFI very complicated. C# has an extremely rich native-interop history (having historically been used to integrate closely with Windows C++ applications) and therefore this approach raised some serious challenges.
In some sense, async is the cost for clean interop with the C/system ABI. Transition across OS threads requires something like async.