Ассемблер - самый понятный и ёмкий язык для устройств. Почему компиляторы не могут посмотреть, что за команды используются в коде будущей программы, и исключить из библиотек всё ненужное, и переконвертировать из современного высшего языка в ассемблер?
Для начала, компиляторы и так, как правило, генерируют или ассемблер, или машинный код. Компиляторы языков вроде C++ и Rust генерируют машинный код процессоров, компиляторы Java и C# — код виртуальной машины. Бывают транспиляторы, которые переводят код с одного высокоуровневого языка на другой, например TypeScript в JavaScript, но они нам сейчас не интересны.
Программы из библиотек собираются уже не компиляторами, а компоновщиками («линковщиками», «линкерами»), при этом компоновка может быть как статической, так и динамической. При статической компоновке в исполнимый файл добавляется как код, написанный программистом, так и код библиотек. При динамической исполнимый файл содержит только код, написанный программистом + ссылки на библиотеки с зависимым кодом. В этом случае недостающие библиотеки загружаются операционной системой (или виртуальной машиной) при запуске программы.
Динамическая компоновка позволяет нескольким программам зависеть одновременно от одних и тех же библиотек, что экономит место на диске и оперативную память.
При статической компоновке компоновщик старается включить в целевой файл только те средства библиотек (функции, классы…), которые либо непосредственно упоминаются в коде программиста, либо упоминаются в коде используемых средств.
Библиотеки обычно стараются делать более универсальными, поэтому в них помещают возможности, охватывающие самые разные сценарии использования. И, порой, вырезать что-то одно маленькое получается невозможно — одна функция прямо или косвенно упоминает всю библиотеку.
Попробуйте скопировать одну (произвольную) статью в Википедии в документ Word, а потом скопировать все статьи, на которые из исходной есть ссылка, а потом статьи по ссылкам из скопированных и т.д. Сотня страниц, не меньше выйдет.
А попытаться исключить всё не нужное, что реально не используется в программе, во-первых, очень сложно, а во-вторых, невозможно.
Очень сложно, т.к. нужно анализировать и менять саму логику программы. Например, есть в библиотеке такой фрагмент кода:
if (источник данных == диск)
открыть файл и прочитать
else if (источник данных == сеть)
подключиться к серверу и попросить данные
else
сообщить «Не указан источник данных»
Вторая ветка программы предполагает зависимость от ещё одной библиотеки, библиотеки подпрограмм работы с сетью
И, допустим, программа, использующая эту библиотеку, всегда предполагает, что данные всегда находятся на диске (переменная «источник данных» всегда «диск»). В этом случае к программе всё равно будет подключена сетевая библиотека, т.к. она упоминается в этом фрагменте кода. Но она использоваться не будет.
Чтобы не подтягивать в данном случае сетевую библиотеку компоновщик и компилятор должны быть достаточно умны, чтобы (а) выяснить, что вторая ветка кода никогда не будет вызываться и (б) суметь её вырезать. Если библиотека доступна в виде исходных текстов, то компилятор может (в теории) её вырезать. А если библиотека уже скомпилирована в машинный код, то вырезать её будет гораздо труднее — дополнительно потребуется сложный анализ машинного кода.
А исключить неиспользуемый код невозможно, поскольку определение недостижимости — алгоритмически неразрешимая задача. Т.е. невозможно написать анализатор кода, который для произвольной программы и произвольной строки кода в ней даст однозначный ответ вроде «строчка кода номер NNN никогда не выполняется» или «строчка кода номер NNN выполняется».
Можно написать анализатор, который допускает три ответа: «данная строчка никогда не выполняется», «данная строчка выполняется» и «ответ дать невозможно», и чем сложнее анализатор, тем реже он будет давать последний ответ. Но полностью исключить его нельзя, это как асимптота, к которой график функции бесконечно стремится и никогда её не достигает.
Но это теория. На практике обычно достаточно неточного ответа: «участок кода однозначно недостижим» / «недостижимость кода доказать невозможно».