We compile the globals and functions in the program into a list of
global labels and definitions. Add a bunch of instruction and etc. types
here for formatting. Then, assemble all of this together in the program.
The program realigns the stack (probably overkill?) and then calls into
the entry point symbol, then exits.
I added ivy_exit so that I wouldn't have to refer to any
platform-specific symbols in the assembly that's emitted, all of that
can be handled by the runtime. Unfortunately there will still be
platform specific behavior because of different calling conventions.