Probably most, if not all, of the "real world" compilers that produce
assembler are derived, culturally if not physically, from the Unix compilers.
Traditional assemblers are so expensive that generating assembler code was
not a great idea. One can argue that it isn't a great idea anyway: if you
know what instruction you want to emit, why not emit it? There isn't a
whole lot of difficulty in turning emit("mov (r0)+, (r1)+") into emit(012021).

However, there are some arguments in favor. One is that generating assembler
keeps the knowledge of object-module format -- usually complex -- in one
place, with a simpler text format used as the interchange medium. This line
of argument may weaken a bit if complex symbol-table info has to be passed
from compiler into object module somehow. Another argument against is that
on modern machines, the assembler may do quite a bit of work, like picking
the precise form for span-dependent instructions or arranging code to match
pipeline constraints. (A friend of mine once commented that the assembler
for the 68000 was more complicated than the code generator for it!) Putting
all that into each compiler isn't trivial, and is a bit stupid given that
the assembler has to exist anyway. This is especially true if the language
has an in-line-assembler facility which requires a full assembler somewhere
in the pipeline anyway. Yet another consideration is that it's a whole lot
easier to deal with assembler output for compiler debugging.

The Minix system in fact uses (slightly compacted) assembler as its *object
module* format, with the assembler and linker rolled into one for building
the final code. This is a rather extreme approach, but an interesting one,
provided that the assembler-linker is fast.

My personal opinion is that on a system with an efficient assembler,
generating binary is probably still a win, but not a very big one, and
generating assembler is definitely easier. There are better things for
compiler writers to spend their time on than reinventing the assembler.

Henry Spencer @ U of Toronto Zoology
{allegra,ihnp4,decvax,pyramid}!utzoo!henry
[My limited experience agrees with this. For example, the Wizard C compiler,
forerunner of Turbo C, could generate either assembler or object. Originally,
there were separate versions of pass 2 depending on which you wanted to
get. In the last release, he combined them and the combined version was only
10% bigger than the previous object-only version. -John]
--