Nice clear description of tail call optimisation. The coroutine section seemed too brief to me. Is it accurate to say that a coroutine is just a subroutine with multiple entry points and possibly some state? I thought a coroutine needed at least enough state to resume where it left off, but resumption was not mentioned.
You are right.
By definition ("coroutine" was defined by Melvin E. Conway in 1963, who has also introduced the FORK/JOIN for processes a.k.a. tasks a.k.a. threads), a coroutine is something that you do not CALL (except perhaps for the first time, but initialization can also be handled in a state argument of RESUME), but you RESUME, repeatedly.
The state of the coroutine is not necessarily stored by the coroutine. "RESUME" may have an argument that stores the state, in which case the code of the coroutine may be re-entrant and stateless.
Subroutines with multiple entry points are just that, not coroutines, though a coroutine may also have multiple entry points.
Many early programming languages, e.g. certain versions of FORTRAN and of PL/I, had subroutines with multiple entries, but they did not have coroutines (PL/I had "tasks" which are what is now called "threads").
What "certain versions" of Fortran since Fortran II did not have the ENTRY statement?
Enforcing modularity at a component level could be a thing ...