27
$\begingroup$

I would like to ask a few questions about Assembly language. My understanding is that it's very close to machine language, making it faster and more efficient.

Since we have different computer architectures that exist, does that mean I have to write different code in Assembly for different architectures? If so, why isn't Assembly, write once - run everywhere type of language? Wouldn't be easier to simply make it universal, so that you write it only once and can run it on virtually any machine with different configurations? (I think that it would be impossible, but I would like to have some concrete, in-depth answers)

Some people might say C is the language I'm looking for. I haven't used C before but I think it's still a high-level language, although probably faster than Java, for example. I might be wrong here.

$\endgroup$
13
  • 10
    $\begingroup$ What research have you done? We expect you to do research before asking, to help you ask a better question. There's lots written on assembly language. $\endgroup$
    – D.W.
    Commented Sep 7, 2015 at 16:56
  • 4
    $\begingroup$ We expect you to do a significant amount of research/self-study before asking, and to tell us in the question what research you have done. In this case, research might include reading relevant Wikipedia articles (e.g., on assembly language and computer architecture) and reading a computer architecture textbook. To make this a better question: do that research, if you haven't already, and then edit the question to explain the research you've done. Often that kind of research helps you formulate a better question; and in any case it helps answerers avoid repeating what you already know. $\endgroup$
    – D.W.
    Commented Sep 7, 2015 at 17:02
  • 17
    $\begingroup$ Start with understanding that/why there is no language called Assembly. $\endgroup$
    – Raphael
    Commented Sep 7, 2015 at 18:41
  • 4
    $\begingroup$ This is more of a social problem than a technical one - you need to convince all CPU manufacturers to make their CPUs accept the same machine language. (Actually, x86 was almost going to be this, by chance - then smartphones took off) $\endgroup$ Commented Sep 8, 2015 at 0:38
  • 3
    $\begingroup$ Nobody seems to have addressed the important misconception expressed in the last sentence about how "fast" different programming languages are. It's important to understand that languages are just an abstraction from the actual process that takes place when you run a piece of code. The comparison is imperfect, but you might imagine that saying "C is a faster language than Java" is a bit like saying that "French is a faster language than German". $\endgroup$
    – mintchkin
    Commented Sep 9, 2015 at 15:31

12 Answers 12

48
$\begingroup$

Assembly language is a way to write instructions for the computer's instruction set, in a way that's slightly more understandable to human programmers.

Different architectures have different instruction sets: the set of allowed instructions is different on each architecture. Therefore, you can't hope to have a write-once-run-everywhere assembly program. For instance, the set of instructions supported by x86 processors looks very different from the set of instructions supported by ARM processors. If you wrote an assembly program for an x86 processor, it'd have lots of instructions that are not supported on the ARM processor, and vice versa.

The core reason to use assembly language is that it allows very low-level control over your program, and to take advantage of all of the instructions of the processor: by customizing the program to take advantage of features that are unique to the particular processor it will run on, sometimes you can speed up the program. The write-once-run-everywhere philosophy is fundamentally at odds with that.

$\endgroup$
11
  • 1
    $\begingroup$ I think that question is already answered by the 3rd paragraph of my answer. As you said, such a scheme would not be efficient, so it'd be fundamentally at odds with the core reason to use assembly language. $\endgroup$
    – D.W.
    Commented Sep 7, 2015 at 17:17
  • 28
    $\begingroup$ @nTuply As soon as you modify your assembly language to cater for different machines, it's become a high-level language with horribly assembly-style syntax. Once you've decided to use a high-level language, you may as well use one with friendlier syntax and let the compiler do the hard work. $\endgroup$ Commented Sep 7, 2015 at 18:01
  • 17
    $\begingroup$ It's not a completely stupid idea to have an "assembly language" that is translated for different machines, because that's basically what LLVM's "IR" is. However, for the reasons David gives, you don't normally write LLVM assembly. Also because 99 times out of 100 you'd do a worse job of writing it than clang does of translating your C to LLVM. Assembly languages are potentially more efficient than high-level languages, but in the hands of most actual programmers with a typical amount of time available to optimise, they don't reach their potential anyway. $\endgroup$ Commented Sep 7, 2015 at 19:10
  • 9
    $\begingroup$ @nTuply, that exists. The process of going from that extra-assembly language to machine instructions is called compilation. $\endgroup$ Commented Sep 7, 2015 at 20:39
  • 3
    $\begingroup$ @PJTraill There is no reason whatsoever to write a compiler in assembler on a modern system, except for the very first bootstrapping step (and most of the time, not even then). Compilers written in a high-level language are vastly more likely to actually be maintainable. Also compare How can a language whose compiler is written in C ever be faster than C?. The purpose of a compiler is to translate from one language (the source language) to another (usually machine language for a specific architecture and OS); this can be written in any language. $\endgroup$
    – user
    Commented Sep 8, 2015 at 19:36
14
$\begingroup$

The DEFINITION of assembly language is that it is a language that can be translated directly to machine code. Each operation code in assembly language translates to exactly one operation on the target computer. (Well, it's a little more complicated than that: some assemblers automatically determine an "addressing mode" based on arguments to an op-code. But still, the principle is that one line of assembly translates to one machine-language instruction.)

You could, no doubt, invent a language that would look like assembly language but would be translated to different machine codes on different computers. But by definition, that wouldn't be assembly language. It would be a higher-level language that resembles assembly language.

Your question is a little like asking, "Is it possible to make a boat that doesn't float or have any other way to travel across water, but has wheels and a motor and can travel on land?" The answer would be that by definition, such a vehicle would not be a boat. It sounds more like a car.

$\endgroup$
2
  • 1
    $\begingroup$ C has often been described as "portable assembly language." $\endgroup$ Commented Sep 9, 2015 at 20:20
  • 2
    $\begingroup$ @LarryGritz Sure. And when C was invented, it was groundbreaking: It offered much of the power of assembly language with the ease of use a compiled. But by definition, it's still a compiled language $\endgroup$
    – Jay
    Commented Sep 10, 2015 at 6:53
9
$\begingroup$

There is no conceptual (I daresay, no computer science) reason against having one assembly language for all computers in the world. In fact, that would make many things much easier. As far as theory is concerned, they are all the same, anyway, up to some funky bijection.

In practice, however, there are different chips for different purposes, with different operations and design principles (e.g. RISC vs CISC) that serve different goals, and the instruction sets that operate them and therewith the assembly languages differ. In the end, the answer is the same as when asking why there are so many different programming languages: different goals, different design decisions.

That said, you can of course introduce levels of abstraction to get to some shared interface. x86, for instance, has been done away with on chip level for quite some time; there's a little piece of hardware that translates x86 instructions to whatever your processor really works with. Languages like C would be another step away from the hardware (if an arguably tiny one), all the way up to languages like Haskell, Java or Ruby. Yes, compiler are one of the main achievements of computer science because they make it possible to separate concerns in this fashion.

$\endgroup$
3
  • 7
    $\begingroup$ "if an arguably tiny one" -- there's your two kinds of programmer right there. Those who consider C a low-level language because its basic operations look a lot like the kinds of things that appear in CPU instruction sets, and those who consider C a high-level language because it's not the same instruction set as the machine. $\endgroup$ Commented Sep 7, 2015 at 19:17
  • $\begingroup$ If by assembly language you mean one giving complete control over the machine code generated for a specific type (or family) of hardware, it would be possible to define one language ”for all computers” in our world at a given moment, but it would have to keep changing. It would admittedly (if well designed) shorten the learning curve for coding for a new architecture, but I expect any job you would want to do with it rather than a compiler would only apply to a tiny fraction of architectures. That computers are the same at an abstract level is a red herring, it is about machine code. $\endgroup$
    – PJTraill
    Commented Sep 9, 2015 at 10:54
  • $\begingroup$ Well, yes, IF all computers had the same instruction set, then you could write one assembly language that would run on all of them. But that's a big IF. IF everyone in the world spoke English, then I could go anywhere in the world and people would understand what I say. IF pigs could fly, then they'd have wings. $\endgroup$
    – Jay
    Commented May 19 at 16:13
7
$\begingroup$

You mention the phrase "write once run anywhere" without seeming to notice its significance. That is the marketing slogan for Sun Microsystems that commercially invented the concept of a "virtual machine" and "bytecodes" for Java, although possibly the idea may have originated in academia 1st. The idea was later copied by Microsoft for .Net after they were successfully sued by Sun for breach of infringement of Java licensing. Java bytecodes are an implementation of the idea of cross-machine assembly or machine language. They are used for several other languages than Java and can theoretically be used to compile any language. After many years of very advanced optimization, Java comes close in performance to compiled languages showing the goal of high performance platform-agnostic virtual machine technology is achievable in general.

Another new idea in early stages/ circulating related to your requirements is called the recomputation project and is for scientific research although could be used for other purposes. The idea is to make computational experiments replicable via virtual machine technology. This is mainly the idea of simulating different machine architectures on arbitrary hardware.

$\endgroup$
3
  • 8
    $\begingroup$ Sun did not invent either virtual machines or byte code, they weren't even the first group to make money off them. Look up p-code. $\endgroup$
    – jmoreno
    Commented Sep 8, 2015 at 0:09
  • $\begingroup$ @jmoreno: he might also want to look up Smalltalk. $\endgroup$ Commented Sep 8, 2015 at 2:47
  • $\begingroup$ the article does not claim sun invented virtual machines/ byte code. there is other history not cited but alluded to. btw another key technology very relevant here: google native client (chrome feature) $\endgroup$
    – vzn
    Commented Sep 10, 2015 at 3:54
5
$\begingroup$

High level reasons

When you think about it, a microprocessor does an amazing thing: it lets you take a machine (such as a washing machine or an elevator), and replace a whole chunk of custom-designed mechanisms or circuits with a cheap, mass-produced silicon chip. You save a lot of money on parts, and a lot of time on design.

But hang on, a standard chip, replacing countless custom designs? There can't be a single, perfect microprocessor that is perfect for every application. Some applications need to minimise power usage but don't need to be fast; others need to be fast but don't need to be easy to program, others need to be low cost, etc.

So, we have many different "flavours" of microprocessor, each with its own strengths and weaknesses. It is desirable for them to all use a compatible instruction set, because this allows code reuse and makes it easier to find people with the right skills. However, the instruction set does affect the cost, complexity, speed, ease-of-use, and physical constraints of the processor, and so we have a compromise: there a few "mainstream" instruction sets (and many minor ones), and within each instruction set there are many processors with different characteristics.

Oh, and as technology changes, all these trade-offs change, so instruction sets evolve, new ones emerge, and old ones die. Even if there was a "best" instruction set of today, it might not be in 20 years.

Hardware details

Probably the biggest design decision in an instruction set is the word size, i.e. how large a number the processor can "naturally" manipulate. 8-bit processors deal with numbers from 0-255, whereas 32-bit processors deal with numbers from 0 to 4,294,967,295. Code designed for one needs to be completely rethought for another.

It is not just a matter of translating instructions from one instruction set to another. A completely different approach may be preferable in a different instruction set. For example, on an 8-bit processor a lookup table may be ideal, while on a 32-bit processor an arithmetic operation would be better for the same purpose.

There are other major differences between instruction sets. Most instructions fall into four categories:

  • Computation (Arithmetic and logic)
  • Control Flow
  • Data Transfer
  • Processor configuration

Processors differ in what sort of computations they can perform, as well as how they approach control flow, data transfer, and processor configuration.

For example, some AVR processors can neither multiply nor divide; whereas all x86 processors can. As you may imagine, eliminating the circuitry required for tasks like multiplication and division can make a processor simpler and cheaper; these operations can still be performed using software routines if they are needed.

x86 allows arithmetic instructions to load their operands from memory and/or save their results to memory; ARM is a load-store architecture and thus only has a few dedicated instructions for accessing memory. Meanwhile x86 has dedicated conditional-branch instructions, while ARM allows practically all instructions to be conditionally executed. Also, ARM allows bit-shifts to be performed as part of most arithmetic instructions. These differences lead to different performance characteristics, differences in internal design and cost of the chips, and differences in programming techniques at the assembly language level.

Conclusion

The reason it is impossible to have a universal assembly language is that, to properly convert assembly code from one instruction set to another, one must design the code all over again—something computers cannot yet do.

$\endgroup$
1
  • $\begingroup$ Excellent answer! People do not understand well enough that computing things that need to be programmed are everywhere among us. It's not just the applications we see running on our screens. How many billions of chips are manufactured each year? $\endgroup$
    – phs
    Commented Sep 11, 2015 at 9:17
4
$\begingroup$

Adding to marvelous answer by D.W.: if you would like to have one assembler it would need to maintain all architectures, perfect translator among them and fully understand what are you doing.
Some heavily optimized codes per one architecture would need to be deoptimized, understood at more abstract level and optimized per another.
But if this was possible we would have perfect C compiler, and writting in pure assembly would not be beneficial at all.
The main point of using assembler is performance, which cannot be squeezed from recent compilers.
Writing such program would be even harder than existing compilers and maintaining all new architectures that are being created would make it even harder.
And for "one only" program, it would also mean full backwards compatibility.

$\endgroup$
5
  • $\begingroup$ For the vast majority of cases, gcc performs better optimization than a programmer can. The main point of using assembler is to do stuff you can't do in C like accessing registers. If you look at the Linux source tree that's pretty much what they use assembly for. $\endgroup$
    – slebetman
    Commented Sep 8, 2015 at 5:58
  • $\begingroup$ @slebetman - gcc allows you to put a variable into a register without resorting to assembly. $\endgroup$ Commented Sep 8, 2015 at 6:42
  • $\begingroup$ @JirkaHanika: are you talking about CPU registers or special purpose hardware registers addressed with special instructions? I suspect slebetman means the latter. $\endgroup$
    – PJTraill
    Commented Sep 8, 2015 at 8:38
  • $\begingroup$ "All codes" - "GCC does better" = "you use assembler". Yes you can access registers without assembler insertions. $\endgroup$
    – Evil
    Commented Sep 8, 2015 at 8:47
  • $\begingroup$ @PJTraill - Slebetman's comment is generally excellent and should perhaps be incorporated into the answer. But, both of his examples (register access and Linux source tree) are likely to feed common misconceptions rather than that they would be excellent examples of what one cannot do in C with gcc extensions; those should be replaced or omitted. (If there's a HW instruction to do something today, you'll have the corresponding gcc extension a year from now. Not always, but very often. Examples age.) $\endgroup$ Commented Sep 8, 2015 at 9:15
3
$\begingroup$

As noted, LLVM is the closest thing to this so far. A big barrier to a really universal language is going to be fundamental differences related to implicit tradeoffs: concurrency, memory use, throughput, latency, and power consumption. If you write in an explicitly SIMD style, you could be using too much memory. If you write in an explicitly SISD style, you would get suboptimal parallelization. If you optimize for throughput, you hurt latency. If you maximize single-threaded throughput (ie: clock speed), you hurt battery life.

At the very least, the code would need to be annotated with the trade-offs. What may be most important is for the language to have good algebraic/type properties that give the compiler a lot of wiggle room to optimize and detect logical inconsistency.

Then there is the question of undefined behavior. Much of the speed of C and assembly languages come from undefined behavior. If you admit undefined behavior that actually does happen, then you end up handling them as special cases (ie: architecture and context specific hacks).

$\endgroup$
1
$\begingroup$

I'm going to go against every other answer posted so far; they were reasonable at the time, but are now somewhat obsolete. I posit that true answer is, in fact:

It's totally possible, and some very big companies went ahead and did exactly that.

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

In other words, WebAssembly is a bytecode format which is designed to be easy for C++-adjacent languages to compile into, while also being easy to run safely on your browser.

Now, one might argue that WebAssembly isn't an "universal assembly language". First, it's not exactly assembly, in the sense that no processor can run it as-is; its instructions have to be compiled to machine code first, usually with additional overhead in the form of safety checks. Second, it's not inherently universal, in that there are lots of architectures it can't be compiled to (GPUs and FPGAs come to mind).

In that regard, the other answers are still relevant: WebAssembly makes a bunch of trade-offs, sacrificing some performance for safety and portability. Writing WebAssembly and compiling it to regular assembly is always going to produce a less efficient program than directly writing assembly.

I'd still argue that it's pretty damn close on both criteria:

  • It's assembly-like, in that it's a language with a lot of information baked-in. It doesn't care about type information, generics, casting, virtual dispatch, etc. It can only read from a write to a contiguous range of data called linear memory; and since it doesn't know how that memory is structured, it only needs to worry about preventing out-of-bounds accesses; structuring reads and writes is the responsibility of the code being compiled into wasm, much like with regular machine code. Thus, compiling from WebAssembly is a lot cheaper and faster than compiling from C++, which is why your browser can afford to ship with a wasm compiler.

  • It's universal-like, in that it can be made to run on any machine that we'd expect C++ (and every other generalist programming language on the market) to run on. It uses very simple primitives, that match pretty closely to the physical architecture of most CPUs (RAM, registers, branching, etc).

There are other arguments for wasm adoptions (it's open-source, unlike Java bytecode, it's widely-supported, etc), but the two points above are the most relevant to your question.

If what you're looking for is an extremely-low level code format, that can be compiled from most languages and run on most machines, then you want WebAssembly.

$\endgroup$
0
$\begingroup$

Perhaps what you are looking for is a Universal Turning Machine notation where everyone agrees on the symbols for the commands. (https://en.wikipedia.org/wiki/Universal_Turing_machine)

An 'assembler' that translates a Turning Acceptable language to the underlying vendor specific machine code and be build for any of those things we call computers.

In The Art of Computer Programming the there is a example of what this might look like.

But consider the question "why isn't their a commercially available universal language that can be used with all computers" I'd suggest the most dominiant influances are (1) convenience, not all assembly languages are the most convienent to use; (2) economics, providing, incompatibility between machines of different brands and vendors is a business strategy as well as the result of limited resources (time/money) to design machines.

$\endgroup$
5
  • $\begingroup$ The question is asking about an assembly language that can be used to program any computer, not an assembly language that's universal in the sense of "universal Turing machine". $\endgroup$ Commented Sep 8, 2015 at 19:53
  • 1
    $\begingroup$ Church-Turing tells us the UTC can do what any programmable computer can do. Aside from finite physical storage issues. An assembly language for a UTC is quite feasible. But as i said cultural and economic practicality may limit actual implementation and adoption in the marketplace. $\endgroup$
    – Chris
    Commented Sep 8, 2015 at 20:06
  • $\begingroup$ You're missing the biggest problem, which is performance! Why use a language 1000s of times slower just for some lofty goal of being hardware-agnostic? The Turing Machine is a terrible model for practical computing. $\endgroup$
    – Artelius
    Commented Sep 9, 2015 at 12:02
  • 1
    $\begingroup$ Would the commenters care to offer any computer science to back their claims? This is after all the computer science forum. $\endgroup$
    – Chris
    Commented Sep 9, 2015 at 14:14
  • 1
    $\begingroup$ I'm not a CS expert. But what I believe is that the von Neumann architecture is a brilliant piece of engineering that strikes a balance between programmability and performance, whilst the Turing machine's purpose is to show that even the most basic machine can compute anything that a more complex machine could. Sure, you can keep adding more and more features to a Turing machine (more tapes, arithmetic), but then you get the same problem you had in the first place, namely people not agreeing on an instruction set. Plus, the lack of random-access creates big overheads in many algorithms. $\endgroup$
    – Artelius
    Commented Sep 12, 2015 at 12:19
0
$\begingroup$

assumption: compiling and optimising a high-level language L1 to a lower-level language L0 is easier than compiling and optimising a high-level language L2 (higher than L1) to L0; easier in the sense that you supposedly can generate more optimised code when compiling L1 to L0 than L2 to L0.

I think the assumption is probably correct, that is why probably most compilers use a low-level intermediate language (IR/LLVM).

if this is true than use any low-level language L0 and write compilers to translate L0 to other low-level languages. For example, use MIPS instruction set, and compile it to x86, arm, power, ...

-Taoufik

$\endgroup$
1
  • $\begingroup$ So you do not know whether your answer is true? And cannot support it? $\endgroup$
    – Evil
    Commented Nov 19, 2016 at 11:26
0
$\begingroup$

A Universal Assembly Language? Of course. By default, you just endow the language with a giant switch table; if (x86) then ... if (68x00) then ... if (AMD) then ... and maybe factor out the stuff that's in common between CPUs, and provide a facility for plug-ins to add new CPUs.

Essentially, most or all of that is what GAS already does.

You can make a Universal Anything this way; even a Universal Universal (a universal language for universal languages).

The question can be better posed as this: are there universal frameworks for assembly languages; in particular, general syntaxes that can be applied across the board? There are, in fact, 3 in common use, which could each easily be adapted all CPUs - the Intel Syntax (which uses "DB"'s, "DS"'s, ";" to head line comments, ":" after labels), the Motorola Syntax (which uses "FCB"'s, "*" to head line comments, strict formatting for labels and no ":") and the VAX Syntax (which has relative labels, e.g. "1f", "1b" and prefixes directives by dots), the last of which is what GAS uses.

We devised the CAS syntax, which has not (yet) been ported to a wide range of CPUs. It's closer to M4 in terms of directives (and may even be redesigned as a dialect of M4), uses C syntax for assembly-time expressions and operations and is free form (e.g. multiple statements per line permitted). Comments are headed by ";;" or "//" or may be in "/* ... */" form, and it has the "1f" and "1b" relative labels of the VAX syntax).

On the other question that you're asking: why can't CPUs all agree to do the same things or things cut from the same cloth? The answer is simple: it's a free market, they're not supposed to.

The differences between CPUs involve matters that are critical to their design and intended uses! The 8051/2, for instance, has large numbers of bit-sized and byte-sized registers and register windows - very convenient for multi-threaded applications and run-time systems. This is something also common with RISC processors. The CPUs intended for desktops or laptops (like the x86 family) have narrow register sets (and a corresponding bottleneck). The addressing modes are different, some have the equivalent of C's *a++ = b or *a-- = b", for instance. Processors in the x86 family from the 80386 onward have a large infrastructure for handling virtual memory, caching, paging, for distinguishing between different levels of protection, while the 8086 and 80186 do not. The interrupt-handling for different CPUs is different. Some have fixed locations for interrupt handler functions, others have fixed locations only for the function pointers, while others have virtual or paged locations. The level of support by the CPU for "location-independent" programs varies significantly.

Even the instruction sets can have significant differences. A processor with bit-sized registers, for instance, doesn't need a zillion different conditional branch instructions, only a few. Those without bit-sized registers may, instead, have a version of the "compare" (CMP) and "test" (TST) operations, along with a large number of conditional branch instructions.

The destinations for goto's and jumps are addressed in different ways. Some use absolute addresses, some use paged addresses or virtual addresses.

If you're going to try and smoothe over all those differences, then you're mission-creeping assemblers into being compilers, no matter how you try to frame it. That's the fallacious route that so-called "high level assemblers" went down. If you're going to go that way, then you might as well just use a compiler equipped with the ability to inject in-line assembly and (ideally, especially for embedded systems) the ability to inject your own custom run-time system/kernel in place of the compiler's default cookie-cutter run-time system/kernel.

$\endgroup$
0
$\begingroup$

I'm going to be a bit contrary here and say yes, actually, it is possible. Especially so if we restrict ourselves to digital computing based on 8 bit bytes.

Pragmatic, practical, feasible? These are much different and your mileage may vary. However it is certainly possible to

  • Enumerate all machine codes up to a given bit-length
  • Create a constant map from all assembly language to all ISA
  • Create a superset of all overlapping operations
  • Extend with unique entries for remaining operations
  • Label the set of all possible codes with the extended superset operations
  • Use maps to translate from universal codes to device specific codes at runtime initialization or firmware flash
  • Write programs

But good luck with that, friends.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.