In effect, we do much of the work of assembly when the compiler is compiled.
The assembler makes one pass fixing up branch offsets, then squeezes out the space left by branch shortening and dumps out the code along with the load-time fixup information. The assembler also deals with dumping unboxed non-immediate constants and symbols. Boxed constants are created by explicit constructor code in the top-level form, while immediate constants are generated using inline code.
[### The basic output of the assembler is: A code vector A representation of the fixups along with indices into the code vector for the fixup locations A PC map translating PCs into source paths
This information can then be used to build an output file or an in-core function object. ]
The assembler is table-driven and supports arbitrary instruction formats. As far as the assembler is concerned, an instruction is a bit sequence that is broken down into subsequences. Some of the subsequences are constant in value, while others can be determined at assemble or load time.
Assemble Node Form* Allow instructions to be emitted during the evaluation of the Forms by defining Inst as a local macro. This macro caches various global information in local variables. Node tells the assembler what node ultimately caused this code to be generated. This is used to create the pc=>source map for the debugger. Assemble-Elsewhere Node Form* Similar to Assemble, but the current assembler location is changed to somewhere else. This is useful for generating error code and similar things. Assemble-Elsewhere may not be nested. Inst Name Arg* Emit the instruction Name with the specified arguments. Gen-Label Emit-Label (Label) Gen-Label returns a Label object, which describes a place in the code. Emit-Label marks the current position as being the location of Label.