Compilers may output assembly code, linkable object code, or ready-to-run machine code; I've worked with examples of all three types. Although compilers which output assembly code may be more adaptable to different platforms than those which output object code, outputting assembly code is often slower than outputting machine code, and running a program will require the assembly code undergo further processing steps. It really isn't much harder for a compiler to generate machine code than to generate assembly code; in some cases it may be easier.
As for whether to generate linkable or ready-to-run code, it's often faster and easier for a compiler to produce the latter; the only advantage of producing the former is that building a program will require recompiling only the parts that have changed, and then linking everything, versus having to feed everything through the compiler. If projects will generally be large, and the portion that changes on any given build small, then being able to selectively compile the parts of the code that have changed may be a 'win'. If, however, one is apt to want to rebuild most of the program on each build, producing runnable machine code may be faster than going through an intermediate link stage.
An additional benefit of producing runnable machine code is that a compiler may be able to take advantage of things it knows about the locations of objects in ways that would not be possible when generating relocatable code. For example, if foo
and bar
are declared in separate modules, a statement foo=bar;
would require something like:
ldr r0,[pc+(_addr_of_bar-*)]
ldr r1,[pc+(_addr_of_foo-*)]
ldr r2,[r0]
str r2,[r1]
...somewhere within 2K of the above:
_addr_of_bar: dw _bar
_addr_of_foo: dw _foo
If variables foo
and bar
happen to be at known addresses which are within 4K of each other [e.g. 1600 bytes apart], the code could be simplified to something like:
ldr r0,[pc+(_addr_of_foo_bar-*)]
ldr r1,[r0+600]
str r1,[r0-1000]
...somewhere within 2K of the above:
_addr_of_foo_bar: dw _foo+1000
Such optimizations are only possible if the compiler knows how things are going to be placed.