Чи справді авторам-компіляторам потрібно «зрозуміти» машинний код? [зачинено]


10

Це може бути дивним питанням.

Хлопець, що пише компілятор C ++ (чи будь-яку мову, що не належить до VM): Чи потрібно йому вміти читати / писати машинну машину? Як це працює?

EDIT: Я конкретно маю на увазі компілятори, що компілюють машинний код, а не якусь іншу мову програмування.



1
Ні. Вам це навіть не потрібно знати, ви можете просто сліпо, безглуздо скопіювати свою цільову специфікацію ISA: dl.acm.org/citation.cfm?id=1706346
SK-логіка

1
Coffescript компілюється в JavaScript.
Картик

@Kartik Чи включає компілятор CoffeeScript, який компілює Javascript, також компілятор Javascript, який компілює все те, до чого складається Javascript? Або він компілюється лише у вихідний код Javascript і більше нічого?
Авів Кон

Компілятор coffeescript просто перетворює cofeescript у JavaScript. Javascript не компілюється, ним обробляється браузер. Я хотів сказати, що ви можете написати компілятор, який збирає одну мову на іншу, для цього вам не потрібно знати машинну мову. Інший приклад - компілятор SPL, який компілює п’єси Шекспіра на C ++. shakespearelang.sourceforge.net
Картик

Відповіді:


15

Ні, зовсім ні. Цілком можливо (і часто навіть бажано) ваш компілятор замість цього випускати код складання. Потім асемблер піклується про створення фактичного машинного коду.

До речі, ваше розмежування не-VM реалізації від VM не корисно.

  • Для початку, використання VM або попередня компіляція до машинного коду - це просто різні способи реалізації мови; у більшості випадків мова може бути реалізована за допомогою будь-якої стратегії. Мені довелося один раз користуватися перекладачем C ++ .

  • Крім того, багато віртуальних машин, таких як JVM, мають двійковий машинний код і деякий асемблер, як і звичайна архітектура.

LLVM (який використовується компіляторами Clang) тут заслуговує на особливу згадку: він визначає VM, для якого інструкції можуть бути представлені як байтовий код, текстова збірка або структура даних, що дозволяє дуже легко випускати з компілятора. Отже, хоча це було б корисно для налагодження (і щоб зрозуміти, що ви робите), вам навіть не доведеться знати про мову складання, лише про API LLVM.

Приємне в LLVM - це те, що його VM - це лише абстракція, і що байтовий код зазвичай не інтерпретується, а натомість прозоро JITted. Таким чином, цілком можливо написати мову, ефективно складену, не знаючи про набір інструкцій вашого процесора.


І ще одна приємна властивість LLVM - це те, що не потрібно глибоко розуміти цільовий ISA, щоб реалізувати ефективний запуск. Це досить декларативно, тому можна майже скопіювати та вставити специфікацію ISA у файли .td, навіть не намагаючись її зрозуміти.
SK-логіка

Дякую за відповідь. Питання: З вашої відповіді та інших відповідей я розумію, що автору-компілятору не потрібно розуміти машинний код, він може використовувати інший інструмент, який робить для нього фактичний код перетворення в машину. Однак хлопець, який написав цей інструмент , мав зрозуміти машинну мову, правда? Хлопець, який написав програмне забезпечення, яке здійснює фактичне перетворення з якоїсь мови в машинну код, має насправді розуміти машинну мову?
Авів Кон

5
@Prog так. Якщо ви створюєте шар абстракції, вам потрібно лише зрозуміти шар під вами. Хоча корисно мати базове розуміння всіх шарів, це насправді не потрібно. Для використання транзистора вам не потрібно розуміти квантову фізику. Вам не потрібно розуміти дизайн мікросхем, щоб використовувати процесор. Вам не потрібно знати машинний код, щоб написати збірку. Вам не потрібно знати набір інструкцій платформи під час використання VM тощо. Але комусь довелося будувати ці рівні абстракції нижче вашого.
amon

1
@ SK-логіка Неправда (принаймні, якщо ви хочете гарного коду) з того, що я чув. Люди, які впровадили сервер Aarch64 для llvm, мали досить багато викликів (переїзди для одного, шаблони завантаження магазинів жахливі, ..). І це НЕ звертаючи уваги на найбільший слон в кімнаті: модель пам'яті АБИ і модель пам'яті мови ви зацікавлені в Ви можете працювати над компілятором, але ви не можете працювати на бекенд без розуміння архітектури ...
Voo

1
Я додам, що насправді немає принципової різниці між збіркою та машинним кодом, концептуально. Ви насправді не матимете великої користі, коли дізнаєтесь, як використовувати певну інструкцію, що таке опкод цієї інструкції. Якщо ви знаєте, як використовувати MOV, це насправді не має значення, якщо ви не знаєте, що це інструкція 27, що, на мою думку, схоже на те, що описує логіка @ SK.
whatsisname

9

Ні. Ключовим моментом вашого питання є те, що збірка є надзвичайно широким терміном. Компіляція може відбуватися з будь-якої мови на будь-яку мову. А збірний / машинний код - лише одна з багатьох мов цілі компіляції. Наприклад, мови Java та .NET, такі як C #, F # і VB.NET, всі компілюються в якийсь проміжний код замість коду, що відповідає певній машині. Не має значення, якщо він працює тоді на VM, мова все ще компілюється. Існує також можливість компілювати на якусь іншу мову, наприклад, C. C - насправді досить популярна ціль компіляції, і багато інструментів це роблять. І нарешті, ви можете використовувати якийсь інструмент або бібліотеку, щоб зробити важку роботу з створення машинного коду для вас. Наприклад, є LLVM, який може зменшити зусилля, необхідні для створення самостійного компілятора.

Крім того, ваше редагування не має жодного сенсу. Це як запитати "Чи повинен кожен інженер зрозуміти, як працює двигун? І я запитую про інженерів, що працюють на двигунах". Якщо ви працюєте над програмою або бібліотекою, яка випромінює машинний код, то вам доведеться це зрозуміти. Справа в тому, що вам не потрібно робити подібного при написанні компілятора. Багато людей робили це перед вами, тому для цього потрібно мати серйозні причини.


А людина, що пише інструмент чи бібліотеку, яка робить фактичне перетворення на машинну мову, мусить зрозуміти машинну мову повністю, правда?
Авів Кон

3
@Prog Чи потрібно повністю розуміти мову програмування, щоб програмувати в ній? Ні, але ви, можливо, будете писати недооптимальний код, і ви не можете робити певних речей, які можуть зробити інші. Чи потрібно вам повністю зрозуміти мову машини, якщо ви пишете компілятор, що перекладається на це. Ні, але ваш компілятор буде неоптимальний і нездатний робити певні речі.
Sumurai8

@ Sumurai8: хоча ви певною мірою можете "зрозуміти" машинну мову кусочно, щоб написати емітер машинного коду, який розуміє всю справу краще, ніж ви. Наприклад, якщо ви пишете хороший фреймворк, ви можете налаштувати визначення кожного коду разом з його витратами та конвеєрними міркуваннями, і тоді ваша рамка може написати оптимізований машинний код, навіть якщо ви не маєте досвіду в оптимізації конкретної машини. Можливість грамотно запрограмувати цей машинний код самостійно, мабуть, не зашкодить.
Стів Джессоп

@SteveJessop Якщо ви розумієте кожен опкод до певної міри, ви можете навчитися машині, як ланцюг цього коду разом з іншими кодами, щоб виразити концепцію вищого рівня, ви розумієте мову машини повністю. Тоді ви просто лінуєтеся знайти оптимальне рішення для кожної проблеми там ;-)
Sumurai8,

@ Sumurai8: хм, але, принаймні, в принципі, я міг би "зрозуміти" кожну опкоду коротко за п’ять хвилин, які знадобиться мені, щоб налаштувати її, а потім забув її до моменту, коли я "зрозумію" опкод після наступного. Мабуть, це не те, що запитуючий означає "вміти читати / писати машинною машиною". Звичайно, я припускаю, що тут досить гарна рамка, це достатньо конфігурується, щоб визначити та використати всю корисну інформацію про кожен опкод набору інструкцій. LLVM дещо спрямований на це, але за словами "Voo" (у коментарі нижче), це не потрапило.
Стів Джессоп

3

Класично компілятор складається з трьох частин: лексичного аналізу, аналізу та генерації коду. Лексичний аналіз розбиває текст програми на мовні ключові слова, імена та значення. Розбирає фігури, як лексеми, що походять з лексичного аналізу, поєднуються в синтаксично правильних для мови твердженнях. Генерація коду приймає структури даних, вироблені парсером, і переводить їх у машинний код або інше представлення. Сьогодні лексичний аналіз та аналіз можуть бути об'єднані в один крок.

Очевидно, що людина, яка пише генератор коду, повинна розуміти цільовий машинний код на дуже глибокому рівні, включаючи набори інструкцій, процесорні трубопроводи та поведінку кешу. Інакше програми, що виробляються компілятором, будуть повільними та неефективними. Вони дуже добре можуть читати і записувати машинні коди, представлені вісімковими або шістнадцятковими числами, але вони, як правило, записують функції для генерування машинного коду, посилаючись внутрішньо на таблиці машинних інструкцій. Теоретично люди, які пишуть лексеру та аналізатор, можуть нічого не знати про генерацію машинного коду. Насправді, деякі сучасні компілятори дозволяють вам підключати власні програми генерації коду, які можуть видавати машинний код для деяких процесорів, про які лексери та аналізатори не чули.

Однак на практиці автори-компілятори на кожному кроці знають багато про різні архітектури процесорів, і це допомагає їм спроектувати структури даних, необхідні для кроку генерації коду.


2

Давно я написав компілятор, який конвертував між двома різними скриптами оболонки. Це не було близько машинного коду.

Запис компілятора повинен розуміти їх вихід , але це часто не машинний код.

Більшість програмістів ніколи не пишуть компілятор, який видає машинний код або код складання, але спеціальні компілятори можуть бути дуже корисними для багатьох проектів для отримання інших результатів.

YACC - один з таких компіляторів, який не виводить машинний код….


0

Не потрібно починати з детального знання семантики мов введення та виводу, але краще закінчити вишукане детальне знання обох, інакше ваш компілятор буде неприпустимо баггі. Отже, якщо ваш вхід - C ++, а ваш вихід - якась конкретна машинна мова, вам, зрештою, потрібно знати семантику обох.

Ось деякі тонкощі компіляції C ++ до машинного коду: (якраз у верхній частині голови, я впевнений, що я ще більше забуваю.)

  1. Якого розміру буде int? "Правильний" вибір тут - це мистецтво, засноване як на природному розмірі вказівника машини, продуктивності АЛУ для різних розмірів арифметичних операцій, так і на виборах, зроблених існуючими компіляторами для машини. Чи машина навіть має 64-бітну арифметику? Якщо ні, то додавання 32-бітових цілих чисел повинно бути перекладом на інструкцію, тоді як додавання 64-бітних цілих чисел повинно бути переведено на виклик функції, щоб зробити 64-бітове додавання. Чи у машині є 8-бітні та 16-бітні операції додавання чи вам доведеться імітувати ті, що мають 32-бітні ops та маскування (наприклад, DEC Alpha 21064)?

  2. Яку умову виклику використовують інші компілятори, бібліотеки та мови на машині? Чи висуваються параметри на стеку справа вліво або вліво-вправо? Чи входять деякі параметри в регістри, а інші - у стеку? Чи є вставки та плавці в різних регіональних просторах? Чи потрібно для реєстру виділених параметрів обробляти спеціально під час викликів varargs? Які регістри зберігаються за допомогою абонента, а які - для збережених? Чи можете ви здійснити оптимізацію виклику?

  3. Що робить кожна з інструкцій щодо зміни машини? Якщо ви попросите змістити 64-бітове ціле число на 65 біт, що це за результат? (На багатьох машинах результат такий же, як зміщення на 1 біт, на інших результат - "0".)

  4. Яка семантика послідовності пам'яті машини? C ++ 11 має дуже чітко визначену семантику пам'яті, яка обмежує обмеження щодо оптимізації в деяких випадках, але дозволяє оптимізувати в інших випадках. Якщо ви збираєте мову, яка не має чітко визначеної семантики пам’яті (як і кожна версія C / C ++ до C ++ 11 та багатьох інших імперативних мов), вам доведеться вигадувати семантику пам’яті, як ви йдете далі, і зазвичай ви хочете винайти семантику пам'яті, яка найкраще відповідає вашій машинній семантиці.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.