В тупом виде - каждый оператор и инструкция языка в коде компилятора описаны набором машинных команд (точнее там что то в виде функции, генерирующей набор команд в соответствии со входными параметрами).
Есть куча особенностей архитектуры машины, способа адресации памяти и т.п. но в общем виде это буквально вставка кусков кода соответственно ранее сделанному лексическому, синтаксическому и семантическому анализу исходных текстов.
Есть еще оптимизация кода, скорее всего именно это определяет объем и сложность задачи, так как без нее это уже не так сложно.
Над компилятором существует еще ряд задач, которые может понадобиться решить, это работа с несколькими файлами исходников, препроцессор (это наверное стало нормой, некий промежуточный язык, обрабатывающий и преобразовывающий исходный код) или его аналоги, благодаря особенностям реализации и историческим наслоениям, процесс получения бинарного кода делят на этапы - создание объектного кода (когда код файла компилируется в промежуточный набор машинных команд но все еще не исполняемый) и линковка - сборка итогового исполняемого файла из объектного кода и библиотек статической линковки. Формально это достаточно простая операция, идет проверка завершенности, т.е. что каждая переменная или функция определены (компилятор может создавать объектный код использования библиотеки только на основе ее короткого описания, а вот линкер объединяет полученный код с библиотекой).
Иногда добавляют еще несколько стадий, например один язык может генерироваться не сразу в объектный код а в исходные коды другого языка, который уже существующим компилятором генерирует итоговый.
Или наоборот, вместо генерации машинного кода, используется коды выдуманной виртуальной машины, которая будет запускаться (транслироваться в реальном времени в машинный код) на разных архитектурах одинаково (например jvm и clr).
И в догонку, в мире уже столько наворотили языков что разрабатывать и поддерживать под каждый оптимальный компилятор становится сложно, что в мире придумали llvm - набор правил, стандартов, библиотек и утилит, включая байткод виртуальной машины и трансляторы на все архитектуры... к примеру opencl использует llvm как основу