C:\1980S\GCC.TXT
1987
GCC: A Compiler Becomes Shared Infrastructure
GCC combined portable code generation with freely modifiable source, making the compiler itself part of a shared development foundation.
Source code is an impressive declaration of intent. A processor requires rather more specific instructions.
The compiler stands between the two. It has to understand the language, preserve the program’s meaning, and choose instructions that make sense on a particular machine. If it gets the job wrong, an application can fail even when its author’s code is correct. If the compiler is unavailable, the code may be perfectly readable and still be of little immediate use.
GCC made this essential piece of machinery available as software people could inspect, modify, and share. Its history belongs beside the applications of the decade because applications depend on such tools. The foundation generally receives fewer photographs than the building.
A beta with refreshingly direct expectations
Richard Stallman announced the GNU C compiler on March 22, 1987. The release included machine descriptions for VAX and Sun systems, documentation about writing those descriptions, and a C preprocessor implementing the November 1986 ANSI draft. The announcement reported successful self compilation on the Motorola 68020 and recent success on VAX, while openly asking users to find remaining bugs.1
The release chronology matters. GCC’s maintained release table identifies March 22 as version 0.9, the first beta. Version 1.0 followed on May 23, 1987. Calling the March release “GCC 1.0” merges two distinct milestones.2
The original expansion was GNU C Compiler. The broader GNU Compiler Collection name belongs to its expansion into a family of language implementations. Even within the 1980s, the official chronology records a G++ release in December 1987. The first C compiler shouldn’t receive retroactive credit for every language and target later incorporated into the project.2

Richard Stallman, photograph by chrys, 2005. Later portrait. Source; CC BY 2.0. Existing Commons crop and color corrections retained; no additional alterations.
GNU was building a Unix compatible system that users could share and change. A compiler was particularly important to that aim: source availability is far more useful when users have tools that can turn changed source into runnable programs.3
Compilation is several jobs
In a simplified C workflow, the preprocessor first handles directives such as included headers and macros. The compiler then recognizes language constructs, checks their relationships, and represents the program internally. Optimization and code generation transform that representation into instructions suitable for a target processor. An assembler makes object files; a linker combines them with other object files and libraries.
The GCC command can coordinate several of these steps, but that doesn’t make every tool in the chain the same program. In particular, an assembly file isn’t yet the fully linked application.
This division explains some familiar failures. An undeclared name can be a language-level problem. A missing library function may appear at link time even though compilation succeeded. A program that builds but returns the wrong answer requires examining yet another part of the process. The computer’s refusal to cooperate comes in stages.
Original explanatory illustration, 2026. A general pipeline informed by the GCC 1.35 manual, not a screenshot or exhaustive list of passes.
GCC 1.35, released in April 1989, provides a useful period reference for the design. Its manual describes an internal representation called register transfer language, or RTL, used by most compiler passes. RTL expresses operations involving registers, memory, and values in an algebraic form. Its printed notation resembles Lisp lists, though the implementation uses C structures.4
A compiler needs this middle ground. C syntax is organized for programmers. Actual machine instructions are organized around a processor. An internal representation lets the compiler reason about a program without immediately committing every expression to a specific assembly instruction.
One calculation, twice written
Here is an original teaching example, using ordinary unsigned C arithmetic:
unsigned int repeated_sum(unsigned int a, unsigned int b)
{
unsigned int first = a + b;
unsigned int second = a + b;
return first + second;
}
For inputs 7 and 5, each intermediate sum is 12, and the return value is 24. A literal translation might add a and b twice, then add the results.
But the inputs haven’t changed between those two expressions. The compiler can calculate a + b once and reuse its result. This kind of transformation is common subexpression elimination. The GCC 1.35 manual describes such a pass, along with loop optimization, instruction combination, and register allocation.5
In a conceptual intermediate form, the operation might look like this:
t1 = a + b
t2 = a + b
result = t1 + t2
After identifying the repeated expression:
t1 = a + b
result = t1 + t1
These are explanatory pseudo instructions, not actual GCC RTL dumps. They show the reasoning rather than obscuring it with the details of a specific processor. Register allocation then decides where a temporary such as t1 should live. A real processor has a limited set of registers, so the apparently generous supply of temporaries in an internal representation eventually meets a hardware budget.
Choosing unsigned arithmetic makes another detail explicit. Unsigned C values wrap modulo one more than their maximum representable value. The equivalence still holds under that arithmetic: repeating the same sum and then adding the two copies gives the same wrapped result as reusing one sum. Signed overflow raises different questions about C semantics, so it would be a poor accidental complication for this example.
The arithmetic and compiled output for the small inputs were checked with a modern GCC. That check confirms the teaching example’s result; it doesn’t prove which exact instructions GCC 0.9 or 1.35 emitted.
The hard part is knowing when reuse is safe
Now replace the second expression with a call that changes a value, or let one expression read a volatile device register. Identical looking source can conceal different observable behavior. A compiler must preserve the behavior the language requires, not merely produce a result that looks plausible for the input tried in a demonstration.
For example, a device register may report the current state of hardware each time it is read. Treating two reads as one can change the program’s meaning. Similarly, floating point calculations introduce rounding effects that make algebraic rearrangement more delicate than it is on a classroom blackboard.
This is why optimization is an analysis problem. The compiler needs information about what can change, what values remain valid, and what transformations the language permits. Faster code is useful after correctness has survived the trip.
I find that tradeoff more revealing than a vague claim that a compiler “makes programs faster.” It also explains why an optimizer can expose mistakes in a program. Code depending on behavior the language doesn’t guarantee may seem to work under one translation and fail under another. The optimizer isn’t obliged to preserve a coincidence.
Portability needs a place for differences
Supporting another processor doesn’t mean replacing all the language processing and optimization. It also doesn’t mean pretending that processors are interchangeable.
GCC’s machine descriptions provide target-specific information about instructions and how they match internal operations. The accompanying configuration specifies other differences, including registers and calling conventions. The 1987 announcement already highlighted descriptions for the initial targets; the 1989 manual documents the larger framework in detail.14
Suppose one processor can add a value directly from memory while another requires a load followed by a register-to-register operation. Both can implement the same C expression, but the useful instruction sequences differ. A shared representation lets the common parts of the compiler express the operation, while the target’s description guides its eventual realization.
Portability here is work arranged for reuse. A new target still needs careful implementation and testing. Argument passing, data sizes, alignment, and object formats can all cause trouble even after simple arithmetic compiles correctly.
The contributor list in GCC 1.35 reflects that work. It credits Paul Rubin for much of the preprocessor, Leonard Tower for parts of the parser and RTL machinery, and numerous contributors for target support and optimization. It also acknowledges ideas from Jack Davidson and Christopher Fraser’s Portable Optimizer. Stallman’s central role doesn’t erase the other people or the research lineage.6
A compiler you could keep working on
The distribution terms are part of the history, but need period labels too. GCC 1.35 contains GPL version 1, dated February 1989. The earlier release preceded that general license and used earlier GNU copying arrangements. Today’s license text shouldn’t be dropped onto 1987 by habit.7
For this article, the relevant practical point is that recipients could obtain source and work on the compiler, subject to its copying conditions. A port or correction could become a contribution others used. Compiler knowledge didn’t have to stop at a vendor’s closed implementation.
The initial announcement offered both FTP access and a paid beta-test tape from the Free Software Foundation. Free software concerned what people were allowed to do with it; distributing physical media still involved costs.1 Even the grandest principles occasionally arrive by post.
A compiler isn’t improved merely by being available. Bugs must be reported, patches reviewed, and targets maintained. Shared infrastructure needs sustained attention, because everyone depends on it while relatively few people are eager to investigate an instruction recognizer on a Friday afternoon.
The right to rebuild has machinery behind it
GCC’s place in the decade is a combination of technical structure and distribution. Its internal representations and machine descriptions made reuse across targets practical. Its source and copying arrangements made that machinery available to people beyond one supplier.
That combination gave software freedom a concrete tool. Users could have editable source, an editor in which to change it, and a compiler capable of rebuilding the result. Each component made the others more valuable.
The achievement is easy to overlook precisely because it became infrastructure. Every ordinary successful build is the compiler doing a difficult job quietly enough that somebody else gets the credit.
Footnotes
-
Richard Stallman, March 22, 1987 release announcement, archived transcription. ↩ ↩2 ↩3
-
GCC project, release chronology, including 0.9, 1.0, G++ 1.15.3, and GCC 1.35. ↩ ↩2
-
GNU Project, 1983 initial announcement. ↩
-
Free Software Foundation, Using and Porting GNU CC, supplied with GCC 1.35 source distribution,
gcc.texinfo, especially “RTL Representation” and “Machine Descriptions.” ↩ ↩2 -
Same GCC 1.35 manual, “Passes and Files of the Compiler.” The worked C example and pseudo instructions are original explanatory material. ↩
-
Same distribution, manual’s “Contributors to GNU CC.” Contributor roles refer to the state documented by version 1.35. ↩
-
Same distribution,
COPYING, GPL version 1. This is the documented license of the inspected 1989 release. ↩