As an ancient mainframe Assembler programmer, I first learned Assembler from studying the code generated by the compiler. There are traps in compiler languages calling user written Assembler routines. The first one I encountered was a routine that could return to different addresses in the calling program. That worked as long as the address was in the same routine, but if the return address was farther up the calling chain (eg. A > B > C < A). A would crash running B's registers.

The application programmers ended up with a dump and had no idea on how to diagnose the problem. I once walked up to a programmer puzzled by a dump and solved it in about three minutes (practice helps) putting out of joint the nose of the senior programmer who was being consulted.

The compiler library handled this situation just fine, but the guru who wrote the Assembler program insisted that programmers had to be more careful.

I wrote another routine to intercept the calls and use the subroutine library when needed.