Assembly language is converted in to machine language by assembler. Why would a compiler convert high-level language to assembly? Can't it directly convert from the high-level language to machine code?
6 Answers
Other reason for compilers to produce assembly rather than proper machine code are:
- The symbolic addresses used by assemblers instead of hard-coding machine addresses make code relocation much easier.
- Linking code may involve safety checks such as type-checking, and that's easier to do with symbolic names.
- Small changes in machine code are easier to accomodate by changing the assembler rather than the code generator.
-
$\begingroup$ why assembly language is so efficient,though it is also written in english and how processor understands it ? $\endgroup$– CODERSAMOct 4, 2013 at 11:56
-
3$\begingroup$ @CODERSAM Assembly is a formal language, not a natural language. It is very close to machine language. So tranlation does not introduce inefficiencies. $\endgroup$ Oct 4, 2013 at 13:29
-
2$\begingroup$ @CODERSAM The precise meaning is complicated, but something like homomorphism in algebra. When you translate, say "add eax, #2" which is x86 assembly, you can translate it to d7f5 (or whatever else the op-code might be), straightaway, without looking at at the context, without adding any more stuff. Assembly has no abstraction. $\endgroup$ Oct 4, 2013 at 14:46
-
2$\begingroup$ "Assembly has no abstraction" — I'd say label names are already an abstraction (from offsets). Also, the context does play a role: e.g.
add eax,2
can be translated to83 c0 02
or to66 83 c0 02
, depending on latest occurred directive likeuse16
. $\endgroup$– RuslanMay 14, 2019 at 19:49 -
1$\begingroup$ Linking is separate from and happens after assembling. Any type-checking will be based on symbol-type info, like
.type foo, function
that creates metadata in the object file. A compiler can emit that directly instead of.type
directives if it wants to, resulting in the same binary description of symbols in a.o
file. Assembly language proper doesn't have types at all, everything you do is just loading or storing bytes. Implementing types is a matter of choosing the right instructions, like usingaddss
notadd
to treat 4 bytes as a single-precision float, not an int. $\endgroup$ Jul 2, 2020 at 20:44
A compiler does usually convert high-level code directly to machine language, but it can be built in a modular way so that one back-end emits machine code and the other assembly code (like GCC). The code generation phase produces "code" which is some internal representation of machine code, which then has to be converted to a usable format like machine language or assembly code.
-
1$\begingroup$ In addition, if the source may include some assembly code, then a mechanism must be available to translate that inline assembly anyway. $\endgroup$– user4577Oct 2, 2013 at 15:33
-
$\begingroup$ why assembly language is so efficient,though it is also written in english and how processor understands it ? $\endgroup$– CODERSAMOct 4, 2013 at 11:57
-
2$\begingroup$ Assembly language is an "English" description of machine code. $\endgroup$ Oct 5, 2013 at 8:51
Historically a number of notable compilers did output machine code directly. There are some difficulties with doing so, however. Generally someone who's trying to confirm that a compiler is working correctly will find it easier to examine assembly-code output than machine code. Further, it's possible (and was historically common) to use a one-pass C or Pascal compiler to produce an assembly-language file which can then be processed using a two-pass assembler. Generating code directly would require either using a two-pass C or Pascal compiler or else using a single-pass compiler followed by some means of back-patching forward-jump addresses [if a runtime environment makes the size of a launched program available in a fixed spot, a compiler could write a list of patches at the end of the code and have startup code apply those patches at runtime; such an approach would increase the executable size by about four bytes per patch-point, but would improve program-generation speed].
If the goal is to have a compiler that runs quickly, direct code generation can work well. For most projects, however, the cost of generating the assembly-language code and assembling it really isn't a major problem nowadays. Having compilers produce code in a form that can interact nicely with code produced by other compilers is generally a big enough benefit to justify the increase in compilation times.
-
$\begingroup$ Most modern mainstream C/C++ compilers do emit machine code directly, at least for x86; clang/LLVM, MSVC, and ICC. Only GCC still runs
as
as a separate process, with an actual tmp file or pipe containing asm text. Other compilers can produce asm text if you ask them to. These are major projects, not like a microcontroller compiler provided by the vendor, so having two different ways to print instructions (as machine code or as asm text) isn't a big deal to maintain. Probably more work is portability of object file formats and relocations. $\endgroup$ Jul 2, 2020 at 20:35
Even platforms that use the same instruction set may have different relocatable object file formats. I can think of "a.out" (early UNIX), OMF, MZ (MS-DOS EXE), NE (16-bit Windows), COFF (UNIX System V), Mach-O (OS X and iOS), and ELF (Linux and others), as well as variants of those, such as XCOFF (AIX), ECOFF (SGI), and COFF-based Portable Executable (PE) on 32-bit Windows. A compiler that produces assembly language need not know much about object file formats, allowing the assembler and linker to encapsulate that knowledge in a separate process.
See also Difference between OMF and COFF on Stack Overflow.
Usually compilers work internally with sequences of instructions. Each instruction will be represented by a data structure representing it's operation name, operands and so-on. When the operands are addresses those addresses will usually be symbolic references, not concrete values.
Outputting assembler is relatively simple. It's pretty much a matter of taking the compilers internal data structure and dumping it to a text file in a specific format. Assembler output is also relatively easy to read which is useful when you need to check what the compiler is doing.
Outputting binary object files is significantly more work. The compiler writer needs to know how all the instructions are encoded (which can be far from trivial on some CPUS), they need to convert some symbolic references to program counter relative addresses and others in to some form of meta data in the binary object file. They need to write everything out in a format that is highly system specific.
Yes you absolutely can make a compiler that can outputs binary objects directly without writing out assembler as an intermediate step. The question like so many things in software development is whether the reduction in compilation time is worth the extra development and maintenance work.
The compiler i'm most familiar with (freepascal) can output assembler on all platforms but can only output binary objects directly on a subset of platforms.
A compiler should be able to produce an assembler output in addition to the normal relocatable code is for the benefit of the programmer.
One time I just not find the bug in a C program running on Unix System V on an LSI-11 machine. Nothing seemed to work. Finally in desperation I had the protable C compiler excrete an assembler version of its translation. I had finally found the bug! The compiler was allocating more registers than existed in the machine! (The compiler allocated registers R0 through R8 on a machine with only registers R0 through R7.) I managed to work around the bug in the compiler and my program worked.
Another benefit of having assembler output is trying to use "standard" libraries that use a different parameter passing protocols. Later C compilers allow me to set the protocol with a parameter ("pascal" would make the compiler add the parameters in the order given as opposed to the C standard of reversing the order).
Yet another benefit is allowing the programmer to see what an appalling job his compiler is doing. A simple C statement takes about 44 machine instruction. Values are loaded from memory and then quickly discarded. etc, etc, etc ...
I personally believe that having a compiler instead of a relocatable object module is really stupid. While compiling your program, the compiler gathers a great deal of information about your program. It usually stores all this information in something called a Symbol Table. After excreting the assembler code it throws all this information table. The assembler then examines the excreted code and re-collects some of the information the the compiler already had. However assembler know nothing about If statements of For statements or While statements. So all this information is missing. Then the assembler produces the relocatable object module that the compiler did not.
Why???