Assembly is an S-expression-based, human-readable representation of the actual bytecodes that will be emitted for the VM. As such, it is a useful intermediate language both for compilation and for decompilation.
Besides the fact that it is not a record-based language, assembly differs from GLIL in four main ways:
<glil-lexical>which can be a ref or a set.
Assembly is isomorphic to the bytecode that it compiles to. You can compile to bytecode, then decompile back to assembly, and you have the same assembly code.
The general form of assembly instructions is the following:
(inst arg ...)
The inst names a VM instruction, and its args will be embedded in the instruction stream. The easiest way to see assembly is to play around with it at the REPL, as can be seen in this annotated example:
scheme@(guile-user)> ,pp (compile '(+ 32 10) #:to 'assembly) (load-program ((:LCASE16 . 2)) ; Labels, unused in this case. 8 ; Length of the thunk that was compiled. (load-program ; Metadata thunk. () 17 #f ; No metadata thunk for the metadata thunk. (make-eol) (make-eol) (make-int8 2) ; Liveness extents, source info, and arities, (make-int8 8) ; in a format that Guile knows how to parse. (make-int8:0) (list 0 3) (list 0 1) (list 0 3) (return)) (assert-nargs-ee/locals 0) ; Prologue. (make-int8 32) ; Actual code starts here. (make-int8 10) (add) (return))
Of course you can switch the REPL to assembly and enter in assembly S-expressions directly, like with other languages, though it is more difficult, given that the length fields have to be correct.