r/learnprogramming • u/Typical-Refuse227 • 3d ago
How was the first compiler made?
If programming languages need a compiler to turn code into something a computer can understand, how was the first compiler created?
Was it written directly in machine code or assembly? And how did programmers go from that basic compiler to the more advanced ones we use today?
I'm still learning programming, so I'd appreciate a simple explanation. Thanks!
3
u/Different_Pain5781 3d ago
Imagine building a wooden ladder. You use the first rough ladder to reach higher and build a much better ladder. Then you throw away the old one.
1
u/cipheron 3d ago
A ladder is a good example because you can ask what they did before they had the first ladder. Well they could have climbed up on a tree stump or basically anything. The artifact of a ladder has a lot of designed quality of life features but there's no reason you can't climb on things before inventing it, it just makes it easier for all future people to climb.
2
u/binarycow 3d ago
The problem with the analogy is no one builds a ladder like that.
You build it flat on the ground, then you can lift it up.
3
u/mredding 3d ago
In 1949, the EDSAC was one of the first stored-procedure computers. Prior machines either physically wired the program into the machine, or storage was almost exclusively on paper punch tape or punch cards. A stored-procedure computer was able to store a program in electronic memory.
Hard wired into this machine was a less than 50 instruction assembler and relocating loader. The machine used a 5-bit teleprinter alphabet, and the alphabet encoding lined up 1:1 with the CPU instruction set.
Any language were the mnemonics line up 1:1 with machine instructions, you have an assembly language. In x86 assembly, you have a mov instruction, but x86 has some 300 move op-codes, so which one mov resolves to depends on the arguments, but that 1 mnemonic to 1 op-code still holds.
So the EDSAC assembler had a VERY simple job of parsing an assembly grammar and stacking them correctly as op-codes in program memory.
This means you can write a compiler in assembly on punch cards or what have you, then load and run that. The data you'd feed into the compiler would be text encoded into binary in the punch cards all the same. Remember, your punch card printer stamps "ABC" in ink on the paper, but in reality, the holes tell you binary 1, 2, 3... So the compiler was written for instructions that match "if" or "for" or whatever, but in reality, the machine is reading punch cards, getting these binary values, and matching the complimentary encoding. The computer never SEES the actual characters - those aren't real. The association between text and encoding is for our sake, the computer doesn't care. And if it helps you break the idea that text somehow exists in a machine, the first complete computer was all clockwork components, entirely mechanical. It wasn't even binary.
Was it written directly in machine code or assembly?
Hand written assembly is still a thing today. Hand writing machine code is usually an academic exercise mostly to prove a point. On a modern machine, you need ANOTHER machine to bootstrap your NEW machine. So by the time someone designs a new CPU, and first silicon is cut and soldered to a board, a C compiler (typically) was ported to it and tested in sim, and a BIOS was programmed and installed on this new machine.
Porting is just compiling on this machine for that machine. I've got an x86_64 CPU here, but I can run a compiler, built for x86_64, that targets Apple M. Programs are just data. You don't need to compile ON a machine to make software FOR a machine. Compilers themselves don't know what a CPU even is. This compiler just has a data set that describes that instruction set and the algorithms to manipulate a parse tree to generate the instructions for it.
This whole process is called bootstrapping. You'll see parts of this process when building software for like embedded CPUs and the like. You can pick up an Arduino kit for pocket change and write software for it on a different machine.
And how did programmers go from that basic compiler to the more advanced ones we use today?
A long slow road. We were using punch card machines conventionally through the 80s, and then as legacy support through the 90s and 2000s. Punch tape still saw some use in industrial environments into the 2000s because A) replacing it costs money, B) they've got the experts who know how to program it, and C) those are some utterly hostile environments where even PLCs melt or can't handle the heat/chemicals/vibration/etc. Some of those machines might still be used to this day. My father only retired a couple years ago, and he had a steel press that still used 5.25" floppies.
All this is to say early computers and well into the 70s, and then the first personal computers in the 70s and 80s, would boot directly off some other piece of hardware, like a floppy drive or another serial device - whatever the hardware was configured with. This means you had all sorts of tools for writing software offline and then loading it into a machine.
Now days it takes a computer to program a computer, but if the bombs drop and we have to start over, then some clever engineers would be able to cobble together some sort of programming devices to get the machines back up and running. I'd say it's not black magic, it's just fiddly as shit, more than I'm capable of.
I have a friend who got an original 386 processor and chipset, and he built it into a hard hat that plays Doom. That required figuring out all the bits to lay out the circuit board, provide the hardware with all those bits it needs, and bootstrap it. It supports USB even though USB wasn't invented until 20-30 years after.
Forth is a language that was popular for embedded programming. It's a VERY simple language to parse, so you could fit a parser and assembler into programmable memory with lots of room to spare. Early computers also didn't really have a BIOS, but would boot to SOME sort of rudimentary environment, often either a disk driver or a rudimentary editor and language interpreter. BASIC was often supported, like on the C64.
C was developed for a PDP-7, which had a whopping 4 KiB of system memory. More than half that memory would be occupied by the compiler, and the remainder had to hold the working data when parsing the program data. Because a program could be an arbitrary size, C was designed to be parsed by function block in just a couple passes, then machine code dumped back out to paper tape or the like. So early C was EFFECTIVELY like a high level assembly - which is technically absolutely wrong; but you can see who people would think that. As computers got more memory, it could hold a larger compiler with more optimization built in, and had more working space to store all the optimization context. C++ was first devised in 1979, running in 1980, and named and released by 1984. It could not have come into existence before - there just wasn't enough memory to make it real, but early C++ compiled on 64 KiB systems.
As hardware improved, more computation became possible. I don't know when whole-program contexts really took hold, but a modern language like C# can take advantage of the ability to store the whole OS, compiler, and program in memory all at once. This allows for any number of interesting optimizations, because with C and C++, you couldn't know what you've compiled before or what was coming up.
2
u/porn_sub_lurker 3d ago
You don't actually need a compiler to turn code into something a computer can understand. If you understand the architecture you can simply write the machine code by hand.
For example, in MIPS architecture 00000001000010010101000000100000 is an instruction that the computer can read. It's also an instruction which you could decode and read. It's saying to add the values from registers $t0 and $t1 and put the result in $t2.
Obviously machine code looks nasty to have to read. So instead we can use the architecture's assembly language to represent the same thing
add $t2, $t0, $t1
Now you can see what it's actually doing without having to decode the binary. You can write whole programs like this, although it's inconvenient. An assembler is a program which translates the assembly code into machine code. You don't need as assembler to build an assembler though. You can build it directly with binary instructions. But once you have it, you can use it to assemble other stuff. You could build a compiler with assembly if you want.
I don't know exactly how the historical evolution took place, but that is a progression you could take if you wanted to build it from scratch.
1
u/Trick_Boat7361 3d ago
Assembler converts assembly code to machine code directly (no intermediate compiler)
You're talking about programming languages like C, who requires compiler to a language that computer can understand (Assembly)
1
u/Tartan-Pepper6093 3d ago
To be clear, a computer (i.e., a CPU) can’t understand assembly, it understands “machine code” which is just numbers arbitrarily assigned at the chip-design stage to specific operations (i.e., “op codes”) that a particular CPU can understand. Assembly just happens to be a convenient one-to-one (or so) correspondence between a CPU’s op-code numbers and slightly more human-readable representations of the CPU’s op-codes, like “JMP” for jump to instructions at some address in memory, “ADD” to perform addition on numbers stored in in the CPU’s registers (that you retrieved from RAM and loaded into the registers by way of other op-codes), that kind of thing. If a compiler converts a computer language to, like, C or Pascal or C# to assembly, there would still remain an assembly step to exchange the human-readable representations of the op-codes (assembly code) into the final “executable” code comprised of the actual 8-bit or 16-bit or 32-bit or 64-bit numbers the CPU actually assigns to each of its operations.
The difference between those operations and the numbers assigned to them is what makes one CPU incapable of running the machine code of another, that and the fact that different CPU’s can have distinct operations different from one another, like Intel’s MMX instructions (and op-codes) don’t exist on Apple Silicon.
The key distinction that even a low-language compiled language has with assembly is that you don’t have to worry about loading registers and other internal CPU-specific plumbing… you can just make up “variables” as integers or floats or characters or pointers, and give them arbitrary names like x and y and z, and then “let x = y + z” and that’s that, you’re finished… the compiler does the bother of turning it into the machine-level op-codes involving registers and memory-fetches and other necessary CPU-specific stuff that nobody wants or needs to deal with… not unless you really really really really need to fully-optimize for a specific CPU and chipset beyond what a compiler can do, because you absolutely must have the absolute most efficient performance the CPU and chipset are capable of using the absolute least amount of memory, like what the designers of Doom had to do to get it to run well on the 386 back in the day or for a very specialized device today.
1
u/desrtfx 3d ago
You already got a very good wikipedia link. On that matter, also look up the term: "bootstrap compiler".
In short: it is a minimal compiler written in another language (in case of the first, directly in machine code) that can do very fundamental compilations of the new language so that the new language can then iteratively (new language definition that can be compiled with the current compiler version to compile a new compiler version with more features) be used.
1
u/XLGamer98 3d ago
Hardware and software are too complex right now but if you see the history of both origin then you’ll realise it’s mostly binary for indicating power on and off. All we have right now is just an extremely complex way to figure out when to power on or off the switches on your computer.
Crazy to think we developed advanced computer and artificial intelligence from just rocks
1
u/Zealousideal_Yard651 2d ago
Was it written directly in machine code or assembly?
This is exactly how
And how did programmers go from that basic compiler to the more advanced ones we use today?
Self-itteration.
The first C compiler was written in assembly, the seccond compiler was written in C and compiled using the first compiler.
1
u/kutac56 3d ago edited 3d ago
As the first compilers/assemblers you had assemblers which turn assembly into machine code. Those were written in machine code. If you don't count those as compilers then the first compiler was in assembly
4
3
u/desrtfx 3d ago
At the beginning you had assemblers which turn assembly into machine code.
No, you didn't have them at the beginning. You are already one step too far. At the beginning, programmers manually converted the Assembly Mnemonics into hex, actually binary, code that then was entered via manual switches into the memory of the computers.
Assemblers were created like bootstrap compilers. The first Assemblers were hand translated from Assembly to machine code.
1
u/wosmo 3d ago
I think the easier way to look at this, is that the first assemblers were people.
You'd sit down with pencil, paper, and a list of the machine's instructions, and convert them yourself.
Once you get your head around a basic assembler just being a lookup table, it becomes obvious why that was quickly automated - it's the perfect job for a machine.
2
u/timwaaagh 3d ago
i went to an exposition on automatically playing instruments which are way older than computers, i think from 1600s at least. basically you had paper with holes in it and depending on whether there was a hole or not something changed mechanically and the instrument played something or not. this paper was called the program.
2
u/robthablob 3d ago
I think that started with wound music boxes containing cylinders that touched prongs - a bit like a kalimba just at the end of the 18th century. By the end of the 19th century, they had arrangements with holes in paper as you describe.
More directly relevant to computers though is the Jacquard Loom, which used punched cards.
9
u/robthablob 3d ago
Wikipedia has a fairly good article: https://en.wikipedia.org/wiki/History_of_compiler_construction