Classicamente, um compilador possui três partes: análise lexical, análise e geração de código. A análise lexical divide o texto do programa em palavras-chave, nomes e valores do idioma. A análise mostra como os tokens provenientes da análise lexical são combinados em declarações sintaticamente corretas para o idioma. A geração de código pega as estruturas de dados produzidas pelo analisador e as converte em código de máquina ou alguma outra representação. Atualmente, a análise e análise lexical podem ser combinadas em uma única etapa.
Claramente, a pessoa que escreve o gerador de código precisa entender o código da máquina de destino em um nível muito profundo, incluindo conjuntos de instruções, pipelines de processador e comportamento do cache. Caso contrário, os programas produzidos pelo compilador seriam lentos e ineficientes. Eles podem muito bem ler e escrever código de máquina como representado por números octais ou hexadecimais, mas geralmente escrevem funções para gerar o código de máquina, consultando internamente as tabelas de instruções da máquina. Teoricamente, as pessoas que escrevem o lexer e o analisador podem não saber nada sobre a geração do código da máquina. De fato, alguns compiladores modernos permitem que você conecte suas próprias rotinas de geração de código, que podem emitir código de máquina para alguma CPU de que os gravadores lexer e analisador nunca ouviram falar.
No entanto, na prática, os escritores do compilador em cada etapa conhecem muito sobre diferentes arquiteturas de processador, e isso os ajuda a projetar as estruturas de dados que a etapa de geração de código precisará.