Відповідь @ jalf охоплює більшість причин, але є одна цікава деталь, про яку він не згадує: Внутрішнє ядро, подібне до RISC, не призначене для запуску набору інструкцій на зразок ARM / PPC / MIPS. Податок x86 сплачується не лише в енергоємних декодерах, але певною мірою у всьому ядрі. тобто це не просто кодування інструкцій x86; це кожна інструкція з дивною семантикою.
Давайте зробимо вигляд, що Intel створила режим роботи, де потік інструкцій був чимось іншим, ніж x86, з інструкціями, які безпосередньо відображалися в uops. Давайте також зробимо вигляд, що кожна модель процесора має свою ISA для цього режиму, тому вони все ще можуть змінювати внутрішні елементи, коли їм заманеться, і виставляти їм мінімальну кількість транзисторів для декодування інструкцій цього альтернативного формату.
Імовірно, у вас все одно залишиться однакова кількість регістрів, зіставлених із архітектурним станом x86, тому операційні системи x86 можуть зберігати / відновлювати його на контекстних комутаторах, не використовуючи набір команд для конкретного процесора. Але якщо ми викинемо це практичне обмеження, так, ми могли б отримати ще кілька регістрів, оскільки ми можемо використовувати приховані тимчасові регістри, які зазвичай зарезервовані для мікрокоду 1 .
Якщо ми просто маємо альтернативні декодери без змін на наступних етапах конвеєра (блоки виконання), цей ISA все одно матиме багато ексцентриситетів x86. Це була б не дуже приємна архітектура RISC. Жодна окрема інструкція не була б дуже складною, але деякі інші божевілля x86 все одно були б там.
Наприклад: зсуви вліво / вправо залишають прапорець "Переповнення" невизначеним, якщо кількість зсувів не дорівнює одиниці, і в цьому випадку OF = звичайне виявлення переповнення з підписом. Подібне божевілля для обертається. Однак виставлені інструкції RISC можуть забезпечувати зрушення без прапорів тощо (дозволяючи використовувати лише один або два з декількох uops, які зазвичай входять до деяких складних інструкцій x86). Тож це насправді не є головним контраргументом.
Якщо ви збираєтеся створити цілком новий декодер для RISC ISA, ви можете попросити його вибрати та вибрати частини інструкцій x86, які будуть виставлені як інструкції RISC. Це дещо пом'якшує спеціалізацію ядра x86.
Кодування інструкцій, ймовірно, не було б фіксованого розміру, оскільки одиничні оцифровування можуть містити багато даних. Набагато більше даних, ніж має сенсу, якщо всі Insns однакового розміру. Один мікрозлитий uop може додавати 32-бітний негайний і операнд пам'яті, який використовує режим адресації з 2 регістрами та 32-бітним переміщенням. (У SnB та пізніших версіях лише режими адресації з одним регістром можуть запобігати операціям ALU).
uops дуже великі і не дуже схожі на інструкції ARM із фіксованою шириною. 32-бітний набір інструкцій з фіксованою шириною може одночасно завантажувати лише 16-бітові безпосередні дані, тому для завантаження 32-бітної адреси потрібна пара негайного завантаження низької половини / навантаження високої-безпосередньої пари. x86 не повинен цього робити, що допомагає не бути жахливим, оскільки лише 15 регістрів GP обмежують можливість зберігати константи в регістрах. (15 - це велика допомога для 7 реєстрів, але подвоєння знову до 31 допомагає набагато менше, я думаю, було знайдено деяке моделювання. RSP, як правило, не є загальним призначенням, тому це більше як 15 регістрів GP та стек.)
TL; Анотація DR:
У будь-якому випадку, ця відповідь зводиться до того, що "набір інструкцій x86 - це, мабуть, найкращий спосіб запрограмувати процесор, який повинен мати можливість швидко виконувати інструкції x86", але, сподіваємось, проливає світло на причини.
Внутрішні загальні формати в інтерфейсному та задньому інтерфейсах
Дивіться також режими Micro fusion та адресації для одного випадку відмінностей у тому, що формати інтерфейсу та інтерфейсу uop можуть представляти на процесорах Intel.
Примітка 1 : Є кілька "прихованих" реєстрів, що використовуються як тимчасові мікрокоди. Ці регістри перейменовані так само, як архітектурні регістри x86, тому команди multi-uop можуть виконувати не в порядку.
наприклад, xchg eax, ecx
на процесорах Intel декодується як 3 uops ( чому? ), і найкраще припустимо, що це MOV-подібні uops, які це роблять tmp = eax; ecx=eax ; eax=tmp;
. У такому порядку, оскільки я вимірюю латентність напрямку dst-> src при ~ 1 циклі, проти 2 для іншого шляху. І ці переміщення uops не схожі на звичайні mov
інструкції; вони, схоже, не є кандидатами на ліквідацію мов із нульовою затримкою.
Дивіться також http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/, де згадується про спробу експериментального вимірювання розміру PRF та необхідність врахування фізичних реєстрів, що використовуються для збереження архітектурного стану, включаючи приховані регістри.
У інтерфейсі після декодерів, але перед етапом видачі / перейменування, який перейменовує регістри у файл фізичного реєстру, внутрішній формат uop використовує номери регістрів, подібні до номерів регістра x86, але має місце для адреси цих прихованих регістрів.
Формат uop дещо відрізняється всередині невпорядкованого ядра (ROB і RS), він же back-end (після етапу видачі / перейменування). Файли фізичного реєстру int / FP мають по 168 записів у Haswell , тому кожне поле реєстру в uop має бути достатньо широким для адресування такої кількості.
Оскільки перейменовувач знаходиться там, у HW, нам, мабуть, було б краще використовувати його, замість того, щоб подавати статично заплановані інструкції безпосередньо на задній план. Отже, ми б почали працювати з набором регістрів, великих як архітектурні регістри x86 + тимчасові мікрокоди, не більше того.
Бек-енд розроблений для роботи з інтерфейсним перейменовувачем, який дозволяє уникнути небезпеки WAW / WAR, тому ми не могли використовувати його як впорядкований процесор, навіть якби ми цього хотіли. Він не має блокування для виявлення цих залежностей; це обробляється шляхом видачі / перейменування.
Це може бути акуратно, якби ми могли завантажувати uops в задній кінець без вузького місця на етапі видачі / перейменування (найвужча точка в сучасних конвеєрах Intel, наприклад, 4-шир. На Skylake проти 4 ALU + 2 завантаження + 1 порт для зберігання в задній кінець). Але якщо ви це зробили, я не думаю, що ви можете статично планувати код, щоб уникнути повторного використання реєстру та наступу на результат, який все ще потрібен, якщо помилка кешу затримала навантаження на довгий час.
Отже, нам в значній мірі потрібно подавати uops до етапу видачі / перейменування, можливо, лише минаючи декодування, а не загальний кеш чи IDQ. Тоді ми отримуємо нормальний OoO exec з розумним виявленням небезпеки. Таблиця розподілу регістрів призначена лише для перейменування 16 + декількох цілочисельних регістрів у ціле число PRF із 168 записів. Ми не могли очікувати, що HW перейменує більший набір логічних регістрів на однакову кількість фізичних регістрів; для цього потрібен більший RAT.