Чому MIPS використовує R0 як "нуль", коли ви могли просто створити два регістри XOR, щоб отримати 0?


10

Я думаю, що шукаю відповідь на дрібницьке запитання. Я намагаюся зрозуміти, чому архітектура MIPS використовує явне "нульове" значення в реєстрі, коли ви можете досягти того ж, просто XOR'ing будь-якого регістра проти себе. Можна сказати, що операція вже зроблена для вас; однак я не можу реально уявити ситуацію, коли ви використовували б багато "нульових" значень. Я читав оригінальні документи Геннессі, і це просто призначає нуль фактично без реальних обґрунтувань.

Чи існує логічна причина для жорсткого кодування бінарного призначення нуля?

оновлення: У 8k виконуваного файлу з xc32-gcc для ядра MIPS у PIC32MZ, у мене є один екземпляр "нуля".

add     t3,t1,zero

фактична відповідь: я присудив суму винагороді тому, хто мав інформацію про MIPS та коди умов. Відповідь насправді полягає в архітектурі MIPS для умов. Хоча я спочатку не хотів призначати час на це, я переглянув архітектуру opensparc , MIPS-V та OpenPOWER (цей документ був внутрішнім), і ось короткі висновки. Реєстр R0 необхідний для порівняння на гілках завдяки архітектурі трубопроводу.

  • ціле порівняння проти нуля та гілки (bgez, bgtz, blez, bltz)
  • ціле порівняння двох регістрів і гілки (beq, bne)
  • ціле порівняння двох регістрів і пастки (teq, tge, tlt, tne)
  • ціле число порівняти регістр і негайне і пастка (teqi, tgei, tlti, tnei)

Просто зводиться до того, як апаратне забезпечення виглядає в реалізації. У посібнику MIPS-V на сторінці 68 наведена невиправлена ​​цитата:

Умовні гілки були розроблені так, щоб включати операції порівняння арифметичних між двома регістрами (як це робиться в PA-RISC та Xtensa ISA), а не використовувати коди умов (x86, ARM, SPARC, PowerPC) або лише порівнювати один регістр проти нуля ( Альфа, MIPS) або два регістри лише для рівності (MIPS). Ця конструкція була мотивована спостереженням, що комбінована інструкція порівняння та розгалуження в звичайний конвеєр дозволяє уникнути додаткового стану коду умови або використання тимчасового регістра, а також зменшує розмір статичного коду та динамічний тракт отримання інструкцій. Ще один момент полягає в тому, що для порівняння проти нуля потрібна нетривіальна затримка ланцюга (особливо після переходу до статичної логіки в прогресивних процесах) і так майже така ж дорога, як і порівняння арифметичної величини. Ще одна перевага злитої інструкції порівняння та розгалуження полягає в тому, що гілки спостерігаються раніше в потоковому потоці інструкцій, і тому їх можна передбачити і раніше. Можливо, є перевага для дизайну з кодами умов у випадку, коли можна взяти декілька гілок на основі одних і тих же кодів умов, але ми вважаємо цей випадок порівняно рідким.

Документ MIPS-V не стосується автора цитованого розділу. Дякую всім за їх час та увагу.


6
Ви часто хочете використовувати 0-значущий реєстр в якійсь операції як вихідне значення. Перед цими операціями було б заздалегідь нульовий реєстр, тому продуктивність приносить користь, якщо ви можете просто використовувати наданий нуль, а не створювати його самостійно, коли потрібен. Приклади включають додавання прапора для перенесення.
JimmyB

3
У архітектурі AVR gcc дбає про ініціалізацію r1 до нуля при запуску і більше ніколи не торкається цього значення, використовуючи r1 як джерело, де не може бути використаний безпосередній 0. Тут виділений нульовий регістр компілюється в програмному забезпеченні компілятором з міркувань продуктивності. (Більшість AVR мають 32 регістри, тому відміняти один (два насправді) в сторону не коштує великих витрат щодо можливих переваг продуктивності та розміру коду.)
JimmyB

1
Я не знаю про MIPS, але може бути швидше перенести r0 в інший реєстр порівняно з XORing, який реєструється, щоб очистити його.
JimmyB

Отже, ви не згодні з тим, що нуль настільки часто, що він вартий позиції у файлі реєстру? Тоді, напевно, ти маєш рацію, бо це правда, це суперечливо, і багато ISA вирішили не резервувати нульовий регістр. Як і інша суперечлива функція на той час, як реєструвати вікна, слоти філій, передбачення інструкцій від "старих часів" ... якщо ви плануєте ISA, вам не доведеться їх використовувати, якщо ви вирішите цього не робити.
користувач3528438

2
Можливо, буде цікаво прочитати один із старих статей Berisley RISC, RISC I: зменшений набір інструкцій VLSI Computer . Він показує, як використання жорсткого проводового нульового регістру, R0, дозволяє реалізувати ряд інструкцій VAX та режимів адресації в одній інструкції RISC.
Марк Плотнік

Відповіді:


14

Нульовий регістр на процесорах RISC корисний з двох причин:

Це корисна константа

Залежно від обмежень ISA, ви не можете використовувати літерал в деяких кодуваннях інструкцій, але ви можете бути впевнені, що можете використовувати це r0для отримання 0.

Він може бути використаний для синтезу інших інструкцій

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

Семантика нульового регістру подібна /dev/zeroдо систем * nix: все написане до нього відкидається, і ти завжди читаєш 0.

Давайте подивимось кілька прикладів того, як ми можемо робити псевдоінструкції за допомогою r0нульового регістру:

; ### Hypothetical CPU ###

; Assembler with syntax:
; op rd, rm, rn 
; => rd: destination, rm: 1st operand, rn: 2nd operand
; literal as #lit

; On an CPU architecture with a status register (which contains arithmetic status
; flags), `sub` can be used, with r0 as destination to discard result.
cmp rn, rm     ; => sub r0, rn, rm

; `add` instruction can be used as a `mov` instruction:
mov rd, rm     ; => add rd, rm, r0
mov rd, #lit   ; => add rd, r0, #lit

; Negate:
neg rd, rm     ; => sub rd, r0, rm

; On CPU without status flags,
nop            ; => add r0, r0, r0

; RISC-V's `jal` instruction -- Jump and Link: Jump to PC-relative instruction,
; save return address into rd; we can synthesize a `jmp` instruction out of it.
jmp dest       ; => jal r0, dest

; You can even load from an absolute (direct) address, for a usually small range
; of addresses by using a literal offset as an address.
ld rd, addr    ; => ld rd, [r0, #addr]

Справа MIPS

Я уважніше придивився до набору інструкцій MIPS. Є декілька псевдоінструкцій, які використовуються $zero; їх в основному використовують для гілок. Ось кілька прикладів того, що я знайшов:

move $rt, $rs          => add $rt, $rs, $zero

not $rt, $rs           => nor $rt, $rs, $zero

b Label                => beq $zero, $zero, Label ; a small relative branch

bgt $rs, $rt, Label    => slt $at, $rt, $rs
                          bne $at, $zero, Label

blt $rs, $rt, Label    => slt $at, $rs, $rt
                          bne $at, $zero, Label

bge $rs, $rt, Label    => slt $at, $rs, $rt
                          beq $at, $zero, Label

ble $rs, $rt, Label    => slt $at, $rt, $rs
                          beq $at, $zero, Label

Що стосується того, чому ви виявили лише один екземпляр $zeroреєстру під час розбирання, можливо, ваш демонтажник досить розумний, щоб перетворити відомі послідовності інструкцій у їх еквівалентну псевдоінструкцію.

Чи справді корисний нульовий регістр ?

Ну, мабуть, ARM вважає, що нульовий регістр є досить корисним, що в їхньому (дещо) новому ядрі ARMv8-A, який реалізує AArch64, тепер нульовий регістр є в 64-бітному режимі; раніше не було нульової реєстрації. (Реєстр трохи особливий, хоча в деяких контекстах кодування - це нульовий регістр, в інших він натомість позначає покажчик стека )


Я не думаю, що MIPS використовує прапори, чи не так? Нульовий регістр додає можливість беззастережно читати / записувати обробку певних адрес без огляду на вміст будь-яких регістрів процесора та допомагає полегшити операцію "mov негайний" стиль, але інші Mov можна зробити за допомогою логічного або простого джерела з самим собою .
supercat

1
У самому справі, не існує регістра , які тримають прапори арифметичних, натомість є три команди , які допомагають емулювати загальні умовні гілки ( slt, slti, sltu).
Jarhmander

Дивлячись на набір інструкцій MIPS і враховуючи, що з того, що я розумію, кожна інструкція буде отримана до моменту виконання попередньої інструкції, мені цікаво, чи було б складно мати опкод, який не стосується нічого, а натомість скажу, що якщо виконується інструкція в режимі прямого режиму, а наступна отримана інструкція має цей бітовий зразок, верхні 16 біт операнду будуть взяті з попередньо встановленої інструкції? Це дозволило б 32-бітним операціям негайного режиму керуватися
двословним двоциклетним

... завантажується операнд, а потім третій цикл, щоб фактично його використовувати.
supercat

7

Більшість реалізацій ARM / POWER / SPARC мають прихований регістр RAZ

Ви можете подумати, що ARM32, SPARC тощо не мають 0 реєстру, але насправді вони є! На рівні мікро-архітектури більшість інженерів-дизайнерів процесорів додають 0-регістр, який може бути невидимим для програмного забезпечення (нульовий регістр ARM невидимий) і використовують цей нульовий регістр для впорядкування декодування інструкцій.

Розглянемо типовий сучасний дизайн ARM32, який має реєстр програмного забезпечення невидимий, скажімо, R16 підключений до 0. Враховуючи навантаження ARM32, багато випадків інструкції щодо завантаження ARM32 потрапляють в одну з цих форм (ігноруйте індексацію перед публікацією на деякий час, щоб тримати дискусію простою ) ...

LDR ra, [rb] // NOTE:The ! is optional and represents address writeback.
LDR ra, [rb, rc](!)
LDR ra, [rb, #k](!)

Всередині процесора це розшифровується до загального

ldr.uop ra, rb, rx, rc, #c // Internal decoded instruction format.

перед тим, як перейти до етапу випуску, де читаються регістри. Зауважте, що rx представляє реєстр для списання оновленої адреси. Ось кілька прикладів декодування:

LDR R0, [R1]      ==> ldr.uop R0, R1, R16, R16, #0 // Writeback to NULL. 
LDR R0, [R1, R2]! ==> ldr.uop R0, R1, R1, R2,   #0 // Writeback to R1.
LDR R0, [R1, #2]  ==> ldr.uop R0, R1, R16, R16, #2 // Writeback to NULL.

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

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

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

MIPS зробив правильний вибір

Таким чином, регістр зчитування як нуль майже обов'язковий у будь-якій сучасній процесорі, і MIPS, що робить його видимим для програмного забезпечення, безумовно, є плюсом, враховуючи, як він упорядковує внутрішню логіку декодування. Дизайнерам процесорів MIPS не потрібно додавати додатковий реєстр RAZ, оскільки 0 доларів уже є основними. Оскільки RAZ доступний для асемблера, MIPS доступно багато інструкцій з psuedo, і це можна подумати як просування частини логіки декодування до асемблера замість створення виділених форматів для кожного типу інструкцій, щоб приховати регістр RAZ від програмного забезпечення як і в інших архітектурах. Реєстр RAZ - хороша ідея, і тому ARMv8 скопіював його.

Якби ARM32 мав регістр $ 0, логіка декодування стала б простішою, а архітектура була б набагато кращою з точки зору швидкості, площі та потужності. Наприклад, із трьох версій LDR, представлених вище, знадобиться лише 2 формати. Аналогічно, немає необхідності резервувати логіку декодування для інструкцій MOV та MVN. Також CMP / CMN / TST / TEQ стане зайвим. Також не було б необхідності розмежовувати коротке (MUL) і довге множення (UMULL / SMULL), оскільки коротке множення може розглядатися як довге множення з високим регістром, встановленим на $ 0 і т.д.

Оскільки MIPS спочатку був розроблений невеликою командою, простота дизайну була важливою, і тому $ 0 явно було обрано у дусі RISC. ARM32 зберігає безліч традиційних особливостей CISC на архітектурному рівні.


1
Не всі процесори ARM32 працюють так, як ви описуєте. Деякі мають нижчу продуктивність для більш складних інструкцій щодо завантаження та / або для повернення до реєстру. Таким чином, вони не можуть всі розшифрувати точно так само.
Пітер Кордес

6

Застереження: я не знаю асемблера MIPS, але 0-значущий регістр не властивий цій архітектурі, і я думаю, він використовується так само, як і в інших архітектурах RISC, яких я знаю.

XORing реєстру для отримання 0 обійдеться вам однією інструкцією, тоді як використання попередньо визначеного 0-значення регістра не буде.

Наприклад, mov RX, RYінструкція часто реалізується як add RX, RY, R0. Без реєстру значень 0, вам доведеться xor RZ, RZкожного разу використовувати mov.

Інший приклад - cmpінструкція та її варіанти (наприклад, "порівняти і стрибати", "порівняти і перемістити" тощо), де cmp RX, R0використовується для перевірки на від'ємні числа.


1
Чи будуть якісь - або проблеми , які реалізують , MOV Rx,Ryяк AND Rx,Ry,Ry?
supercat

3
@supercat Ви не зможете кодувати mov RX, Immабо mov RX, mem[RY]якщо ваш набір інструкцій підтримує лише одне негайне значення та один доступ до пам'яті за кожну інструкцію.
Дмитро Григор’єв

Я не знайомий з режимами адреси MIPS. Я знаю, що в ARM є [Rx + Ry << шкала] і [Rx + disp] режими, і хоча можливість використання останнього для деяких абсолютних адрес може бути корисною в деяких випадках, як правило, це не суттєво. Прямий режим [Rx] може бути емульований за допомогою [Rx + disp], використовуючи нульове переміщення. Для чого використовується MIPS?
supercat

movє поганим прикладом; ви можете реалізувати його з одразу 0 замість нульового регістра. напр ori dst, src, 0. Але так, вам знадобиться опкод для mov-негайної реєстрації, якщо у вас його не було addiu $dst, $zero, 1234, як-от, luiале для нижнього 16 біта замість верхнього 16. І ви не змогли використовувати norабо subстворити однооперанд не / neg .
Пітер Кордес

@supercat: якщо ви все ще дивуєтесь: класичний MIPS має лише один режим адресації: register + disp16. Сучасні MIPS додали інші опкоди для 2-регістрових режимів адресації для FP навантажень / магазинів, прискорюючи індексацію масиву. (Але все ще не для цілого завантаження / зберігання, можливо тому, що це може вимагати більше портів для читання у файлі реєстру цілих чисел для двох адресних регістрів + регістр даних для магазину. Див. Використання регістра як зміщення )
Пітер Кордес,

3

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

Здійснення фактичного xor вимагає трохи сил і часу, щоб переключити ворота і потім зберегти їх у реєстрі, навіщо платити цю вартість, коли наявне значення 0 може бути легко доступним.

Сучасний процесор також має (прихований) 0-регістр значень, який вони можуть використовувати як результат xor eax eaxінструкції через перейменування реєстру.


6
Реальна вартість R0полягає не в заземленні декількох проводів, а в тому, що вам потрібно зарезервувати код для цього в кожній інструкції, що стосується реєстрів.
Дмитро Григор’єв

Хор - червона оселедець. xor-нулінг хороший лише для x86, де процесори розпізнають ідіому та уникають залежності від входів. Як ви зазначаєте, сімейство Сендібридж навіть не працює для неї взагалі, просто обробляє її на етапі реєстрування-перейменування. ( Який найкращий спосіб встановити реєстр на нуль у складі x86: xor, mov або і? ). Але на MIPS, XORing регістра матиме помилкову залежність; Правила впорядкування пам'яті (HW-еквівалент C ++ std::memory_order_consume) вимагають, щоб XOR поширював залежність.
Пітер Кордес

Якщо у вас не було нульового реєстру, ви б додали опкод для негайного переміщення до реєстру. Подобається, luiале не зміщується ліворуч на 16. Отже, ви все одно можете вписати невелику кількість в регістр з однією інструкцією. Дозволити лише нуль з помилковою залежністю було б шалено. (Звичайні MIPS створюють ненульові значення з addiu $dst, $zero, 1234або ori, тому ваш аргумент "витрати на енергію" виходить з ладу. Якщо ви хочете уникнути запуску ALU, ви додасте опкод для mov-негайної реєстрації замість того, щоб мати програмне забезпечення ADD або OR одразу з нулем.)
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.