Skip to content
KinPilot™Learn & Explore
Menu

Grades 9–12 · Computing

How can a compiler turn one program into instructions for different processors?

A compiler turns a program’s meaning into instructions a chosen processor can execute. It does not usually replace each source word with one machine instruction. Instead, it builds several representations that expose structure, check rules and support transformations. Consider the expression y = x * 2 + 1, using unsigned 32-bit arithmetic. Multiplication happens before addition, so the structure says to multiply x by 2 and then add 1. A front end first recognizes names, numbers and operators, organizes them into a syntax tree, and checks their types. Knowing the type matters: integer and floating-point operations have different rules.

A simplified intermediate representation can write the calculation as t = mul i32 x, 2 followed by y = add i32 t, 1. Here i32 denotes a 32-bit integer, and t names a temporary result. These two lines reveal the order and data dependence without committing to a particular physical register or processor. The notation is deliberately abbreviated, not a complete LLVM function.

An optimizer may replace multiplication by 2 with a left shift by one bit. For this unsigned 32-bit example, both retain the same low 32 bits, including wraparound. The transformation would need fresh justification under different language or arithmetic rules. Optimization is a proof obligation about allowed behavior, not permission to substitute a result that merely looks plausible.

The back end selects instructions supported by a target processor and assigns temporary values to its limited registers or memory. One hypothetical target could use MUL r0, 2 and ADD r0, 1. Another could use SHL r0, 1 and ADD r0, 1. The displayed instruction spellings are teaching notation rather than instructions for a named commercial processor. Both examples assume x is initially in r0. For x = 7, the source expression gives 15. The intermediate operations give 14 and then 15, and either illustrated target sequence gives the same result. One test does not prove compiler correctness for every input; the unsigned arithmetic rule explains why this particular replacement works generally within its stated bounds.

The syntax tree places multiplication below addition. The paired register traces show different instruction choices with the same illustrated result.

A compiler translates one expressionA syntax tree gives multiplication before addition for y equals x times two plus one. Intermediate instructions and two hypothetical register sequences retain the example result fifteen for x seven.One meaning, several formsy = x × 2 + 1+×1x2Unsigned 32-bit exampleIntermediate formt = mul i32 x, 2y = add i32 t, 1Target instruction choicesTarget AMUL r0, 2ADD r0, 1Target BSHL r0, 1ADD r0, 17 → 14 → 157 → 14 → 15Target names are illustrative.For this unsigned rule, ×2 andone-bit left shift agree.
The expression y = x × 2 + 1 becomes intermediate operations and two hypothetical target instruction sequences. At x = 7, each produces 15.

A shared intermediate representation allows different language front ends to reuse analysis and different processor back ends to reuse earlier work. The enduring requirement is preservation of defined behavior. A compiler’s achievement is to change representation and execution strategy while keeping the program’s promised meaning.

About this explanation

Starting knowledge: Know that source code expresses instructions in a language and that a processor executes machine instructions.

Sources

  • LLVM Project — Writing an LLVM Backend
  • LLVM Project — Kaleidoscope: Code generation to LLVM IR
← Grades 9–12 topics